序
本文展示一個(gè)常見的取消線程的方法。
錯(cuò)誤實(shí)例
class BrokenPrimeProducer extends Thread {
private final BlockingQueue<BigInteger> queue;
private volatile boolean cancelled = false;
BrokenPrimeProducer(BlockingQueue<BigInteger> queue) {
this.queue = queue;
}
public void run() {
try {
BigInteger p = BigInteger.ONE;
while (!cancelled){
queue.put(p = p.nextProbablePrime());
}
} catch (InterruptedException consumed) {
}
}
public void cancel() {
cancelled = true;
}
}
這里試圖用一個(gè)標(biāo)志來跳出while循環(huán),理論上貌似可行,但是這里使用的是阻塞的操作,那么就出現(xiàn)一種場景,線程永遠(yuǎn)阻塞在put方法,根本就沒來得及下個(gè)循環(huán)去判斷cancelled這個(gè)條件,造成永遠(yuǎn)無法停止掉線程。
正確方法
通過中斷來取消線程。
public class PrimeProducer extends Thread {
private final BlockingQueue<BigInteger> queue;
PrimeProducer(BlockingQueue<BigInteger> queue) {
this.queue = queue;
}
public void run() {
try {
BigInteger p = BigInteger.ONE;
while (!Thread.currentThread().isInterrupted()){
queue.put(p = p.nextProbablePrime());
}
} catch (InterruptedException consumed) {
/* Allow thread to exit */
}
}
public void cancel() {
interrupt();
}
}
這里的關(guān)鍵是queue的put操作能夠響應(yīng)interrupt方法,拋出InterruptedException,倒不是因?yàn)閣hile條件里頭的isInterrupted,這里while條件換成boolean可以照樣可以。
小結(jié)
調(diào)用interrupt并不意味著立即停止目標(biāo)線程正在進(jìn)行的工作,而只是傳遞了請求中斷的消息。對中斷操作的正確理解是:它并不會真正地中斷一個(gè)正在運(yùn)行的線程,而只是發(fā)出中斷請求,然后由線程在下一個(gè)合適的時(shí)刻中斷自己。
有些方法,例如wait、sleep和join等,將嚴(yán)格地處理這種請求,當(dāng)它們收到中斷請求或者在開始執(zhí)行時(shí)發(fā)現(xiàn)某個(gè)已被設(shè)置好的中斷狀態(tài)時(shí),將拋出一個(gè)異常。設(shè)計(jì)良好的方法可以完全忽略這種請求,只要它們能使調(diào)用代碼對中斷請求進(jìn)行某種處理。
設(shè)計(jì)糟糕的方法可能會屏蔽中斷請求,從而導(dǎo)致調(diào)用棧中的其他代碼無法對中斷請求作出響應(yīng)。在使用靜態(tài)的interrupted時(shí)應(yīng)該小心,因?yàn)樗鼤宄?dāng)前線程的中斷狀態(tài)。如果在調(diào)用interrupted時(shí)返回了true,那么除非你想屏蔽這個(gè)中斷,否則必須對它進(jìn)行處理—可以拋出InterruptedException,或者通過再次調(diào)用interrupt來恢復(fù)中斷狀態(tài)。