java執行緒中斷機制
利用標誌變數
通過檢查volatile型別的標誌變數控制執行緒中斷,不使用volatile型別的標誌變數會導致jvm對記憶體的優化引起死迴圈
public class StopThread {
private static volatile boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while (!stopRequested) {
i++;
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
interrupt中斷
如果不通過像上述這樣的標誌變數中斷執行緒,也可以利用Thread類的相關方法進行中斷
header 1 | header 2 |
---|---|
public static boolean interrupted | 測試當前執行緒是否已經中斷。執行緒的中斷狀態 由該方法清除。換句話說,如果連續兩次呼叫該方法,則第二次呼叫將返回 false(在第一次呼叫已清除了其中斷狀態之後,且第二次呼叫檢驗完中斷狀態前,當前執行緒再次中斷的情況除外)。 |
public boolean isInterrupted() | 測試執行緒是否已經中斷。執行緒的中斷狀態不受該方法的影響。 |
public void interrupt() | 中斷執行緒。 |
Thread.interrupt方法可以中斷執行緒正在執行的會丟擲InterruptedException的方法時,執行interrupt方法的時候該執行緒會丟擲異常,以異常的方式中斷執行緒,比如下面這個例子
例一
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(60);
} catch (InterruptedException e) {
//執行後續清理
System.err.println("this thread has been notified...");
}
}
});
thread.start();
//等待讓thread執行緒開始執行
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
System.out.println("finish");
}
sleep方法會丟擲InterruptedException,sleep方法是一個可中斷的方法,上面這個例子thread執行緒處於sleep狀態時,mian執行緒呼叫interrupt方法中斷了正在睡眠的執行緒.在catch中務必進行資源的清理工作,thread.interrupt()方法不能中斷正在等待鎖或者正在等待I/O的執行緒.如果想要關閉I/O等待的資源可以直接關閉底層資源來終止執行緒,而對於鎖等待的執行緒終止在下面會有案例.在這個例子中尤其要注意一點,InterruptedException異常被捕獲之後,中斷狀態會被重設為false
例二
Java中斷機制是一種協作機制,也就是說通過中斷並不能直接終止另一個執行緒,而需要被中斷的執行緒自己處理中斷。在下面這個例子中,我們檢查Thread類的標誌變數的方法來中斷執行緒
class Compute implements Runnable {
@Override
public void run() {
double d = 1.0;
while (!Thread.interrupted()) {
d = d + (Math.PI + Math.E) / d;
}
}
}
public class NonInterrupTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Compute());
thread.start();
TimeUnit.SECONDS.sleep(2);
thread.interrupt();
System.out.println("interrupt...");
}
}
再強調下上文提到的一點Thread.interrupted()方法在呼叫之後Thread的標誌變數會被複位
ReentrantLock
上文提到對於正在進行鎖等待的執行緒,我們無法通過Thread.interrupt方法立即中斷執行緒,那麼有沒有一種鎖可以在被interrupt時中斷鎖等待呢?實際上我們可以通過lockInterruptibly方法實現
class BlockMutex {
private Lock lock = new ReentrantLock();
public BlockMutex() {
lock.lock();
}
public void f() {
try {
lock.lockInterruptibly();
System.out.println("lock acquire in f() ");
} catch (InterruptedException e) {
//資源清理
e.printStackTrace();
}
}
}
class MyBlockThread implements Runnable {
private BlockMutex blockMutex;
public MyBlockThread(BlockMutex blockMutex) {
this.blockMutex = blockMutex;
}
@Override
public void run() {
System.out.println("waiting lock in f()");
blockMutex.f();
System.out.println("broken out of blocked call");
}
}
public class MyLockInterruptTest {
public static void main(String[] args) throws InterruptedException {
//執行緒建立BlockThread同時就持有了blockMutex鎖
BlockMutex blockMutex = new BlockMutex();
Thread thread = new Thread(new MyBlockThread(blockMutex));
thread.start();
TimeUnit.SECONDS.sleep(1);
//中斷鎖等待
thread.interrupt();
}
}
主執行緒在建立BlockMutex時就持有物件鎖,另一執行緒在等待獲取BlockMutex物件鎖時是就中斷該執行緒