如何正確停止線程
使用 interrupt 來通知思喊,而不是強(qiáng)制
1:普通情況停止線程
public class RightWayStopThreadWithoutSleep implements Runnable {
@Override
public void run() {
int num = 0;
while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
if (num % 10000 == 0) {
System.out.println(num + "是1W的倍數(shù)");
}
num++;
}
System.out.println("任務(wù)運(yùn)行結(jié)束壁酬!");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
thread.start();
Thread.sleep(1000);
// 通知停止線程
thread.interrupt();
}
}
通知停止線程
thread.interrupt();
并且線程需要配合
Thread.currentThread().isInterrupted()
運(yùn)行結(jié)果:
……
……
221730000是1W的倍數(shù)
221740000是1W的倍數(shù)
221750000是1W的倍數(shù)
221760000是1W的倍數(shù)
221770000是1W的倍數(shù)
221780000是1W的倍數(shù)
221790000是1W的倍數(shù)
221800000是1W的倍數(shù)
任務(wù)運(yùn)行結(jié)束!
Process finished with exit code 0
2:線程可能被阻塞情況下停止線程
public class RightWayStopThreadWithSleep {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
while (num <= 300 && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍數(shù)");
}
num++;
}
try {
// 等個(gè)1秒恨课,模擬阻塞
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("線程已停止S咔恰!");
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
// 等待時(shí)間要小于上面設(shè)置的1秒剂公,不然線程都運(yùn)行結(jié)束了希俩,才執(zhí)行到下面的thread.interrupt();代碼
Thread.sleep(500);
// 通知停止線程
thread.interrupt();
}
}
線程在sleep 1秒的過程中,收到interrupt信號被打斷纲辽,
線程正在sleep過程中響應(yīng)中斷的方式就是拋出 InterruptedException 異常
運(yùn)行結(jié)果:
0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
線程已停止Q瘴洹!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:19)
at java.lang.Thread.run(Thread.java:748)
Process finished with exit code 0
3:線程在每次迭代后都阻塞的情況下停止線程
public class RightWayStopThreadWithSleepEveryLoop {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
try {
while (num <= 10000) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍數(shù)");
}
num++;
// 每次循環(huán)都要等待10毫秒拖吼,模擬阻塞
Thread.sleep(10);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
// 5秒后通知停止線程
Thread.sleep(5000);
thread.interrupt();
}
}
當(dāng)每次迭代都會讓線程阻塞一段時(shí)間的時(shí)候鳞上,在while/for循環(huán)條件判斷時(shí),是不需要判斷線程是否中斷的Thread.currentThread().isInterrupted()
如果將上述代碼的 try/catch 放在 while 循環(huán)內(nèi)
public class RightWayStopThreadWithSleepEveryLoop {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int num = 0;
while (num <= 10000) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍數(shù)");
}
num++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(5000);
thread.interrupt();
}
}
運(yùn)行結(jié)果:
0是100的倍數(shù)
100是100的倍數(shù)
200是100的倍數(shù)
300是100的倍數(shù)
400是100的倍數(shù)
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at stopthreads.RightWayStopThreadWithSleepEveryLoop.lambda$main$0(RightWayStopThreadWithSleepEveryLoop.java:18)
at java.lang.Thread.run(Thread.java:748)
500是100的倍數(shù)
600是100的倍數(shù)
700是100的倍數(shù)
……
……
會發(fā)現(xiàn)雖然拋出了異常吊档,但是程序并沒有停止篙议,還在繼續(xù)輸出,
即使在while條件判斷處添加 !Thread.currentThread().isInterrupted() 條件籍铁,依然不能停止程序涡上!
原因:
java語言在設(shè)計(jì)sleep函數(shù)時(shí),有這樣一個(gè)理念:就是當(dāng)它一旦響應(yīng)中斷拒名,便會把interrupt標(biāo)記位清除吩愧。
也就是說雖然線程在sleep過程中收到了interrupt中斷通知,并且也捕獲到了異常打印了異常信息增显,但是由于sleep設(shè)計(jì)理念雁佳,導(dǎo)致Thread.currentThread().isInterrupted()標(biāo)記位會被清除,所以才會導(dǎo)致程序不能退出同云。
兩種停止線程最佳方法
1. 捕獲了InterruptedException之后的優(yōu)先選擇:在方法簽名中拋出異常
public class RightWayStopThreadInProd implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
while (true) {
System.out.println("go...");
try {
throwInMethod();
} catch (InterruptedException e) {
// 捕獲異常糖权,進(jìn)行保存日志、停止程序等操作
System.out.println("stop");
e.printStackTrace();
}
}
}
/**
* 如果方法內(nèi)要拋出異常炸站,最好是將異常拋出去星澳,由頂層的調(diào)用方去處理,而不是try/catch
* 這樣調(diào)用方才能捕獲異常并作出其它操作
* @throws InterruptedException
*/
private void throwInMethod() throws InterruptedException {
Thread.sleep(2000);
}
}
如果方法內(nèi)要拋出異常旱易,最好是將異常拋出去禁偎,由頂層的調(diào)用方去處理腿堤,而不是try/catch
這樣調(diào)用方才能捕獲異常并做出其它操作。
2. 在catch中調(diào)用Thread.currentThread().interrupt();來恢復(fù)設(shè)置中斷狀態(tài)
public class RightWayStopThreadInProd2 implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd2());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("程序運(yùn)行結(jié)束");
break;
}
reInterrupt();
}
}
private void reInterrupt() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}