1.很多人經常會用錯interrupt方法,直接看例子
package com.landon.mavs.example.concurrent;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
*
* interrupt容易使用出錯的例子
*
* <pre>
* 1.如何結束BadRunnable這樣的任務.即沒有任務結束條件來保證可以正常關閉它.使用interrupt沒有作用,其不會中斷正在運行的線程
* 2.結論:任務最好不要這樣寫,否則無法正常安全的關閉線程.通常需要在while()中指定任務結束條件如設置volatile變量或者判斷當前線程是否已中斷等或者通過投遞結束消息方式(消息隊列)等
* </pre>
*
* <p>
* <a href=
* "http://docs.oracle.com/javase/6/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html"
* >為何Thread#stop/resume/suspend被棄用</a>
* <p>
*
* @author landon
*
*/
public class InterruptePuzzleExample {
private static final Logger LOGGER = LoggerFactory
.getLogger(InterruptePuzzleExample.class);

public static void main(String[] args) throws Throwable {

// Thread thread = new Thread(new BadRunnable());

// thread.start();
// 執行interrupt,試圖終止無限循環的任務-徒勞
// thread.interrupt();

VolatileRunnable volatileTask = new VolatileRunnable();
Thread volatileThread = new Thread(volatileTask);
volatileThread.start();

// 主線程暫停5s
Thread.sleep(5 * 1000);
// 停止任務,結束volatileThread,在主線程置stopFlag(所以用volatile)
volatileTask.stop();

LOGGER.debug("VolatileRunnable end.");

Thread thread2 = new Thread(new InterruptedRunnbale());
thread2.start();

// 主線程暫停1秒
Thread.sleep(1 * 1000);
// 調用interrupte結束任務->直接中斷處于sleep的任務
thread2.interrupt();

LOGGER.debug("main_thread2 isInterrupted:" + thread2.isInterrupted());

QueueThread qt = new QueueThread();
qt.start();

for (int i = 1; i < 5; i++) {
qt.offerMessage(new QueueMessage(i));
}

// 準備停止qt
qt.prepareDispose();

}

private static class BadRunnable implements Runnable {

@Override
public void run() {
LOGGER.debug("BadRunnable begin.");

// 無限循環
while (true) {
}
}

}

private static class VolatileRunnable implements Runnable {
// 指定volatile(更新即可視) 停止標識
private volatile boolean stopFlag;

public void stop() {
stopFlag = true;
}

@Override
public void run() {
LOGGER.debug("VolatileRunnable begin.");

while (!stopFlag) {
}
}
}

private static class InterruptedRunnbale implements Runnable {

@Override
public void run() {
LOGGER.debug("InterruptedRunnbale begin.");

// 這里判斷調用當前是否已被打斷做判斷
while (!Thread.currentThread().isInterrupted()) {
try {
// 用sleep替代業務邏輯的耗時,可被打斷
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
LOGGER.debug("InterruptedRunnbale is interrupted.");

// 參考Interrupt
// API.類似調用如wait/join/sleep等方法時會收到InterruptedException且中斷狀態被清除
LOGGER.debug("after catch InterruptedException,thread2 isInterrupted:"
+ Thread.currentThread().isInterrupted());
// 因為中斷狀態被清除了.所以這次要再次調用interrupt.設置中斷狀態,然后任務從循環跳出.線程結束
Thread.currentThread().interrupt();
LOGGER.debug("after again execute interrupt,thread2 isInterrupted:"
+ Thread.currentThread().isInterrupted());
}
}
}
}

private static class QueueThread extends Thread {
// 阻塞消息隊列
private LinkedBlockingQueue<QueueMessage> queue = new LinkedBlockingQueue<InterruptePuzzleExample.QueueMessage>();
// 因為這里通過投遞內部消息方式,即在內部單線程執行.所以不用volatile
private boolean stopFlag;

@Override
public void run() {
LOGGER.debug("QueueThread begin.");

while (!stopFlag) {
try {
QueueMessage msg = queue.take();

if (msg != null) {
LOGGER.debug("QueueThread process msg:" + msg);

// -1表示停止消息(注:因為是QueueMessage內部使用,可以直接訪問private屬性)
if (msg.msgType == -1) {
dispose();
}
}

} catch (InterruptedException e) {
LOGGER.debug("QueueMessage is interrupted.take is notify.");
}
}
}

public void offerMessage(QueueMessage msg) {
queue.offer(msg);
}

public void dispose() {
stopFlag = true;
// 這里interrupt可省略,因為既然執行到了dispose,則此時一定未阻塞
// interrupt();
}

// 準備銷毀,由外部線程進行調用
public void prepareDispose() {
LOGGER.debug("QueueThread prepare dispose.");
offerMessage(new QueueMessage(-1));
}
}

private static class QueueMessage {
// 消息類型
private int msgType;

public QueueMessage(int type) {
msgType = type;
}

@Override
public String toString() {
return "QueueMessage [msgType=" + msgType + "]";
}

}
}

2.很多人經常分不清interrupted和isInterrupted兩個方法的區別,看例子
package com.landon.mavs.example.concurrent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
*
* 使用Thread#interrupted/Thread#isInterrupted
*
* <pre>
* 1.個人認為interrupted方法是返回之前的中斷狀態并清除中斷狀態 2.而isInterrupted只是返回線程的中斷狀態而已
* 3.而對于interrupt方法
* ,對于諸如可拋出InterruptedException的一些方法,線程收到InterruptedException后會清除中斷狀態
* ;反之則會設置狀態中斷{仔細參考Thread#interrupt的api doc}{@link InterruptThread3}}
* </pre>
*
* <pre>
* public boolean isInterrupted() {
* return isInterrupted(false);
* }
*
* 靜態方法->針對當前調用線程
* public static boolean interrupted() {
* return currentThread().isInterrupted(true);
* }
*
* private native boolean isInterrupted(boolean ClearInterrupted);
* </pre>
*
* @author landon
*
*/
public class ThreadInterruptedExample {
private static final Logger LOGGER = LoggerFactory
.getLogger(ThreadInterruptedExample.class);

public static void main(String[] args) throws Exception {
InterruptThread it = new InterruptThread();
it.start();

InterruptThread2 it2 = new InterruptThread2();
it2.start();

InterruptThread3 it3 = new InterruptThread3();
// 此時it3阻塞在wait方法內
it3.start();
// 在外部調用iterrupt->it3收到InterruptedException->中斷狀態清除
it3.interrupt();

// true,因為這個是主線程調用的.所以此時it3還未被清除中斷狀態
LOGGER.debug("it3.isInterrupted:" + it3.isInterrupted());
// 做了一個等待.
Thread.sleep(3 * 1000);
// false,此時it3的中斷狀態已經被清楚
LOGGER.debug("it3.isInterrupted:" + it3.isInterrupted());
}

private static class InterruptThread extends Thread {
@Override
public void run() {
// false
LOGGER.debug("InterruptThread before interrupt.#interrupted:"
+ interrupted());
// false
LOGGER.debug("InterruptThread before interrupt.#isInterrupted:"
+ isInterrupted());

// 調用interrupt,這里直接設置了中斷狀態
LOGGER.debug("InterruptThread execute interrupt.");
interrupt();

// true
// 調用了#interrupt->#interrupted返回true->由下面的輸出可以看到,其清除了中斷狀態,所以下面的#isInterrupted返回了false
LOGGER.debug("InterruptThread after interrupt.#interrupted:"
+ interrupted());
// false
LOGGER.debug("InterruptThread after interrupt.#isInterrupted:"
+ isInterrupted());
}
}

private static class InterruptThread2 extends Thread {
@Override
public void run() {
// false
LOGGER.debug("InterruptThread2 before interrupt.#interrupted:"
+ interrupted());
// false
LOGGER.debug("InterruptThread2 before interrupt.#isInterrupted:"
+ isInterrupted());

// 調用interrupt
LOGGER.debug("InterruptThread2 execute interrupt.");
interrupt();

// true 這里#interrupt#->isInterrupted->返回true,即該方法不影響線程的中斷狀態
LOGGER.debug("InterruptThread2 after interrupt.#isInterrupted:"
+ isInterrupted());

// true 這里#interrupted依然返回true并清除了中斷狀態.所以下面的輸出返回false
LOGGER.debug("InterruptThread2 after interrupt.#interrupted:"
+ interrupted());

// false
LOGGER.debug("InterruptThread2.#isInterrupted:" + isInterrupted());

// false 這里再次調用#interrupted->返回了false.因為此時的狀態狀態已經為false了
LOGGER.debug("InterruptThread2.#interrupted:" + interrupted());

}
}

private static class InterruptThread3 extends Thread {
private final Object lock = new Object();

@Override
public void run() {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
LOGGER.debug("InterruptThread3#wait,is interrupted..");
// false
LOGGER.debug("InterruptThread3#wati,receive InterruptedException.#isInterrupted:"
+ isInterrupted());
}
}
}
}
}

3.總結:通過代碼的方式簡單的總結了線程的interrupt,interrupted,isInterrupted三個方法.另外還提供了幾個正確結束線程的簡單方法demo.






























































































































































































2.很多人經常分不清interrupted和isInterrupted兩個方法的區別,看例子














*【landon認為因阻塞的線程被interrupt后,雖然是收到了異常,但是卻中斷了阻塞,其實是可以繼續運行的!所以會清除中斷狀態】

* <pre>
* if (Thread.interrupted()) // Clears interrupted status!
* throw new InterruptedException();
* </pre>




















































































































3.總結:通過代碼的方式簡單的總結了線程的interrupt,interrupted,isInterrupted三個方法.另外還提供了幾個正確結束線程的簡單方法demo.