在線程運(yùn)行的過程中 如何正確的停止線程? 是否可以使用volatile來停止線程?
使用Interrupt
對(duì)于java而言 最正確的停止線程的方式就是Interrupt,但是Interrupt僅僅起到了通知線程的作用 并不會(huì)主動(dòng)的去中斷線程
看一下使用方式
while (!Thread.currentThread().isInterrupted() && more work to do) {
do more work
}
當(dāng)線程調(diào)用interrupt方法之后 這個(gè)線程的中斷標(biāo)記位就被置為true 我們可以通過isInterrupted()
方法來檢測(cè)是否中斷 并且做出響應(yīng)
sleep期間是否可以響應(yīng)到中斷
我們?cè)谑褂弥袛嗟倪^程中 需要思考另一個(gè)問題 就是如果我們的線程sleep或者wait了 是否還可以響應(yīng)中斷? 如果不能響應(yīng)中斷 那么響應(yīng)延后性就太強(qiáng)了
看一下代碼
Runnable runnable = () -> {
int num = 0;
try {
while (!Thread.currentThread().isInterrupted() &&
num <= 1000) {
System.out.println(num);
num++;
Thread.sleep(1000000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
看一下sleep時(shí)是否可以響應(yīng)interrupt的代碼
Runnable runnable = new Runnable() {
@Override
public void run() {
int i = 0;
while (!Thread.currentThread().isInterrupted() && i < 1000) {
i++;
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("線程中斷");
}
}
}
};
Thread thread = new Thread(runnable);
thread.start();
try {
Thread.sleep(5000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
我們發(fā)現(xiàn)是可以響應(yīng)到Interrupt中斷的 當(dāng)我們調(diào)用interrupt時(shí) 線程是可以響應(yīng)到中斷信號(hào)的 并且會(huì)拋出InterruptedException異常 并且重新將中斷信號(hào)置為false java在設(shè)計(jì)之初就考慮到了這種情況 我們應(yīng)該在catch中對(duì)異常做處理或者拋出異常
如果我們?cè)赾atch中不做任何處理 相當(dāng)于把中斷隱藏了 這是非常不合理的
使用volatile來中斷線程
我們可以使用標(biāo)志位來處理 每次循環(huán)都判斷一下標(biāo)志位是否為false 如果為false 則中斷線程
public class VolatileCanStop implements Runnable {
private volatile boolean canceled = false;
@Override
public void run() {
int num = 0;
try {
while (!canceled && num <= 1000000) {
if (num % 10 == 0) {
System.out.println(num + "是10的倍數(shù)。");
}
num++;
Thread.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
VolatileCanStop r = new VolatileCanStop();
Thread thread = new Thread(r);
thread.start();
Thread.sleep(3000);
r.canceled = true;
}
}
但是使用volatile使用過程中 會(huì)有一個(gè)弊端
比如在線程運(yùn)行過程中 處于阻塞狀態(tài) 這時(shí)候我們將canceled標(biāo)志位改了發(fā)現(xiàn)并不生效 因?yàn)榫€程目前已經(jīng)是阻塞狀態(tài)了 需要先變成運(yùn)行狀態(tài) 才可以響應(yīng)到canceled標(biāo)志位
以生產(chǎn)者/消費(fèi)者為例
class Producer implements Runnable {
public volatile boolean canceled = false;
BlockingQueue storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= 100000 && !canceled) {
if (num % 50 == 0) {
storage.put(num);
System.out.println(num + "是50的倍數(shù),被放到倉(cāng)庫(kù)中了省古。");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生產(chǎn)者結(jié)束運(yùn)行");
}
}
}
我們看到 當(dāng)倉(cāng)庫(kù)滿的時(shí)候 BlockingQueue會(huì)阻塞線程 這時(shí)候我們已經(jīng)沒辦法響應(yīng)canceled了 相反 我們可以響應(yīng)interrupt信號(hào)
總結(jié)
總的來看 中斷線程的方法最合適的只有interrupt方法 或者某些場(chǎng)景下 我們可以使用volatile關(guān)鍵字來實(shí)現(xiàn)線程中斷