停止線程
停止線程是java多線程開發(fā)中很重要的技術(shù)點,實際工作中很多業(yè)務(wù)都有類似的需求,掌握此技術(shù)可以對業(yè)務(wù)中所需停止的線程做有效的處理。但是停止線程在java語言中并不像break語句中那樣干脆簡單,還需要我們做一下技巧性的處理。
如何更好的停止一個線程是我們應(yīng)該要考慮的問題,停止一個線程意味著在線程處理完任務(wù)之前停掉正在做的操作,即放棄當(dāng)前的操作。停止一個線程在之前老的JDK中使用的是Thread.stop()方法,但是后面發(fā)現(xiàn)這種處理方法是很危險而且不安全的,由于stop()方法已經(jīng)在JDK中被標(biāo)明是“作廢/過期”的方法,顯然它在功能上是具有缺陷的。作為一個負(fù)責(zé)的java工程師,最好是不要去使用它,因為使用stop()方法釋放鎖將會給數(shù)據(jù)造成不一致性的結(jié)果。如果出現(xiàn)這種情況,程序處理的數(shù)據(jù)可能遭到破壞,最終導(dǎo)致程序執(zhí)行流程錯誤。
大多數(shù)停止一個線程的操作使用的是Thread.interrupt()方法,雖然方法名是“終止,停止”的意思,但是這個方法不會直接終止一個正在運行的線程還需要加入一個判斷才可以完成線程的停止。
此外,在java中有以下的3種方法可以終止正在運行的線程:
- 使用退出標(biāo)志,使線程正常退出,也就是當(dāng)run方法完成后才停止;
- 就是我們上面所說的使用stop方法強(qiáng)行終止線程,是過期作廢的方法,這種方法可以排除不用;
- 使用interrupt方法終止線程。
第一種使用退出標(biāo)志的方法樓主不做介紹,具體可以參考其他博客,都大同小異,樓主寫這篇博客主要是探索使用interrupt停止線程的幾種方法的優(yōu)劣。
異常法停止線程
首先我們用for-break方式停止線程:
public class ExceptionInterrupt extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 500000; i++) {
if (this.isInterrupted()) {
System.out.println("已經(jīng)是停止?fàn)顟B(tài)了!我要退出了!");
break;
}
System.out.println("i=" + (i + 1));
}
System.out.println("我被輸出,如果此代碼是for又繼續(xù)運行,線程并未停止!");
}
public static void main(String[] args) {
ExceptionInterrupt thread = new ExceptionInterrupt();
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end!");
}
}
打印結(jié)果如下:
......
i=421200
i=421201
i=421202
i=421203
i=421204
i=421205
i=421206
i=421207
i=421208
i=421209
已經(jīng)是停止?fàn)顟B(tài)了!我要退出了!
我被輸出,如果此代碼是for又繼續(xù)運行,線程并未停止!
end!
我們發(fā)現(xiàn),上面的示例雖然停止了線程,但是如果for語句下面還有語句,那么還是會繼續(xù)執(zhí)行。所以我們這里用異常法來停止線程,就不會繼續(xù)執(zhí)行for后面的業(yè)務(wù)邏輯了。
public class ExceptionInterrupt2 extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 500000; i++) {
if (this.isInterrupted()) {
System.out.println("已經(jīng)是停止?fàn)顟B(tài)了!我要退出了!");
throw new InterruptedException();
}
System.out.println("i=" + (i + 1));
}
System.out.println("我在for下面");
} catch (InterruptedException e) {
System.out.println("進(jìn)入ExceptionInterrupt2 類中run方法的catch了!");
e.printStackTrace();
}
}
public static void main(String[] args) {
ExceptionInterrupt2 thread = new ExceptionInterrupt2();
thread.start();
try {
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
打印結(jié)果如下:
......
i=96487
i=96488
i=96489
i=96490
i=96491
i=96492
i=96493
已經(jīng)是停止?fàn)顟B(tài)了!我要退出了!
進(jìn)入ExceptionInterrupt2 類中run方法的catch了!
java.lang.InterruptedException
at com.thread.chapter1.ExceptionInterrupt2.run(ExceptionInterrupt2.java:20)
end!
在sleep()中停止線程
如果線程在sleep()狀態(tài)下停止線程,會有什么效果呢?我們來看下面的示例:
public class SleepInterrupt extends Thread{
@Override
public void run() {
super.run();
try {
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("在沉睡中被停止!進(jìn)入catch!"+this.isInterrupted());
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
SleepInterrupt thread=new SleepInterrupt();
thread.start();
Thread.sleep(200);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end");
}
}
執(zhí)行結(jié)果如下:
run begin
end
在沉睡中被停止!進(jìn)入catch!false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.thread.chapter1.SleepInterrupt.run(SleepInterrupt.java:18)
從執(zhí)行結(jié)果來看,如果在sleep狀態(tài)下停止某一線程,會進(jìn)入catch語句,并且清除停止?fàn)顟B(tài)值,使之變?yōu)閒alse。上面的示例是先sleep然后再用interrupt()停止,與之相反的操作也要注意。
public class SleepInterrupt2 extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 100000; i++) {
System.out.println("i=" + (i + 1));
}
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("先停止,再遇到了sleep!進(jìn)入catch!");
e.printStackTrace();
}
}
public static void main(String[] args) {
SleepInterrupt2 thread = new SleepInterrupt2();
thread.start();
thread.interrupt();
System.out.println("end");
}
}
執(zhí)行結(jié)果如下:
......
i=99991
i=99992
i=99993
i=99994
i=99995
i=99996
i=99997
i=99998
i=99999
i=100000
run begin
先停止,再遇到了sleep!進(jìn)入catch!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.thread.chapter1.SleepInterrupt2.run(SleepInterrupt2.java:21)
使用return停止線程
將方法interrupted()與return結(jié)合使用也能實現(xiàn)停止線程的效果。
public class ReturnInterrupt extends Thread {
@Override
public void run() {
while (true) {
if (this.isInterrupted()) {
System.out.println("停止了!");
return;
}
System.out.println("time=" + System.currentTimeMillis());
}
}
public static void main(String[] args) throws InterruptedException {
ReturnInterrupt thread=new ReturnInterrupt();
thread.start();
Thread.sleep(2000);
thread.interrupt();
}
}
執(zhí)行結(jié)果如下:
......
time=1536809054172
time=1536809054172
time=1536809054172
time=1536809054172
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
time=1536809054173
停止了!
小結(jié)
上面幾種方法都是通過使用interrupt停止線程,只是做了不同的技巧處理,不過建議使用“拋異?!钡姆椒▉韺崿F(xiàn)線程的停止,因為在catch塊中還可以將異常向上拋,使線程停止的事件得以傳播。
參考
《java多線程編程核心技術(shù)》