Java多線程(二)
上一篇“Java多線程(一)”主要討論的是線程的創(chuàng)建沙咏,本章主要討論停止線程。
1.概述
停止一個(gè)線程意味著在線程處理完任務(wù)之前停掉正在做的操作撕捍,也就是放棄當(dāng)前的操作钞翔。雖然這看起來很簡(jiǎn)單黑忱,但是必須做好防范措施叶圃,以便達(dá)到預(yù)期的效果袄膏。
在java中有以下3中方法可以終止正在運(yùn)行的線程:
- (1)使用退出標(biāo)志,使線程正常退出掺冠,也就是當(dāng)run方法完成后線程終止沉馆。
- (2)使用stop方法強(qiáng)行終止線程,但是不推薦使用這個(gè)方法德崭,因?yàn)榇朔椒ㄒ呀?jīng)過時(shí)悍及。
- (3)使用interrupt方法中斷線程。
2.使用stop()停止線程
使用stop()停止線程是非常暴力的接癌,但不推薦用它,雖然它確實(shí)可以停止一個(gè)正在運(yùn)行的線程扣讼,但這個(gè)方法是不安全的缺猛,而且已經(jīng)是被棄用作廢的,在將來的java版本中,這個(gè)方法將不可用或不被支持荔燎。
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 5000; i++) {
System.out.println("i=" + (i + 1));
}
}
public static void main(String[] args) {
try {
MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(10);
myThread.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
JDK源碼中將stop()標(biāo)識(shí)為過時(shí)(Deprecated):
@Deprecated
public final void stop() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
checkAccess();
if (this != Thread.currentThread()) {
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
}
...
stop()方法的不良后果
1)調(diào)用 stop() 方法會(huì)立刻停止 run() 方法中剩余的全部工作耻姥,包括在 catch 或 finally 語句中的,并拋出ThreadDeath異常(通常情況下此異常不需要顯示的捕獲)有咨,因此可能會(huì)導(dǎo)致一些清理性的工作的得不到完成琐簇,如文件,數(shù)據(jù)庫等的關(guān)閉座享。
2)調(diào)用 stop() 方法會(huì)立即釋放該線程所持有的所有的鎖婉商,導(dǎo)致數(shù)據(jù)得不到同步,出現(xiàn)數(shù)據(jù)不一致的問題渣叛。
3.使用interrupt方法中斷線程
tips:使用interrupt()方法僅僅是在當(dāng)前線程中打了一個(gè)停止的標(biāo)記丈秩,并不是真正的停止線程。
所以使用interrupt()方法中斷線程還需要結(jié)合判斷線程是不是停止的淳衙,如果判斷線程是中斷狀態(tài)再停止線程蘑秽。Java的SDK提供了兩種方法來判斷線程是否停止:
- 1)this.interrupted():測(cè)試當(dāng)前線程是否已經(jīng)是中斷狀態(tài),執(zhí)行后具有將狀態(tài)標(biāo)志清除為false的功能箫攀。
- 2)this.isInterrupted():測(cè)試線程Thread對(duì)象是否已經(jīng)是中斷狀態(tài)肠牲,但不清除狀態(tài)標(biāo)志。
3.1 兩種判斷線程停止?fàn)顟B(tài)方式的區(qū)別
public static void main(String[] args) {
try {
MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(100);
myThread.interrupt();
System.out.println("是否停止1:"+myThread.interrupted());
System.out.println("是否停止1:"+myThread.interrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
輸出結(jié)果:
是否停止1:false
是否停止1:false
代碼執(zhí)行myThread.interrupt()給myThread線程打上停止標(biāo)記靴跛。然后用interrupted()來判斷線程狀態(tài)缀雳。但從輸出結(jié)果來看,線程并未停止汤求,這也就印證了interrupted()的解釋:測(cè)試當(dāng)前線程是否已經(jīng)中斷俏险,當(dāng)前線程是main線程,從未中斷過扬绪。
將上面的代碼稍作修改:
public class TestInterrupt {
public static void main(String[] args) {
try {
MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(100);
// myThread.interrupt();
Thread.currentThread().interrupt();
System.out.println("是否停止1:"+Thread.interrupted());
System.out.println("是否停止1:"+Thread.interrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
輸出結(jié)果:
是否停止1:true
是否停止1:false
修改后的代碼使用Thread.currentThread().interrupt();中斷了main線程竖独,并且第一次執(zhí)行interrupted()的結(jié)果為true,第二次執(zhí)行的結(jié)果為false挤牛,這也印證了interrupted()具有將狀態(tài)標(biāo)志清除的功能莹痢。
再來看看isInterrupted()方法:
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
myThread.interrupt();
System.out.println("是否停止1:"+myThread.isInterrupted());
System.out.println("是否停止1:"+myThread.isInterrupted());
}
輸出結(jié)果:
是否停止1:true
是否停止1:true
從輸出結(jié)果可以看到,方法isInterrupted()并未清除狀態(tài)標(biāo)志墓赴,所以打印了兩個(gè)true竞膳。
3.2 中斷線程的方法——異常法
public class InterruptMyThread extends Thread {
@Override
public void run() {
super.run();
try{
for(int i=0;i<50000;i++){
if(this.interrupted()){
System.out.println("線程是停止?fàn)顟B(tài)了。诫硕。坦辟。。");
throw new InterruptedException();
}
System.out.println(i);
}
}catch (InterruptedException e){
System.out.println("線程停止章办,跳出for循環(huán)锉走,進(jìn)入catch");
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
InterruptMyThread interruptMyThread = new InterruptMyThread();
interruptMyThread.start();
Thread.sleep(1000);
interruptMyThread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch~");
e.printStackTrace();
}
}
}
輸出結(jié)果:
線程是停止?fàn)顟B(tài)了滨彻。。挪蹭。亭饵。
線程停止,跳出for循環(huán)梁厉,進(jìn)入catch
java.lang.InterruptedException
at com.panda.thread.stop.interrupt.InterruptMyThread.run(InterruptMyThread.java:11)
3.3 中斷線程的方法——return
將方法interrupt()與return結(jié)合使用也能實(shí)現(xiàn)停止線程的效果辜羊。
public class InterruptThreadByReturn extends Thread {
@Override
public void run() {
super.run();
while (true){
if(this.isInterrupted()){
System.out.println("線程停止了!");
return;
}
System.out.println("timer="+System.currentTimeMillis());
}
}
public static void main(String[] args) throws InterruptedException {
InterruptThreadByReturn interruptThreadByReturn = new InterruptThreadByReturn();
interruptThreadByReturn.start();
Thread.sleep(2000);
interruptThreadByReturn.interrupt();
}
}
輸出結(jié)果:
...
timer=1611474998138
timer=1611474998138
timer=1611474998138
timer=1611474998138
線程停止了!
不過還是建議使用“異常法”來實(shí)現(xiàn)線程的停止,因?yàn)榭梢栽赾atch塊中可以對(duì)異常的信息進(jìn)行相關(guān)的處理词顾,而且使用異常流能更好八秃、更方便地控制程序的運(yùn)行流程。
3.4 在沉睡中停止
如果在sleep狀態(tài)下停止某一線程计技,會(huì)拋出InterruptedException異常喜德,并清除停止?fàn)顟B(tài)值。
public class InterruptThreadinSleep extends Thread{
@Override
public void run() {
super.run();
try{
System.out.println("begin");
Thread.sleep(200000);
System.out.println("end");
}catch (InterruptedException e){
System.out.println("進(jìn)入catch--"+this.isInterrupted());
e.printStackTrace();
}
}
public static void main(String[] args) {
try{
InterruptThreadinSleep interruptThreadinSleep = new InterruptThreadinSleep();
interruptThreadinSleep.start();
Thread.sleep(200);
interruptThreadinSleep.interrupt();
}catch (InterruptedException e){
System.out.println("main catch!");
e.printStackTrace();
}
}
}
輸出結(jié)果:
begin
進(jìn)入catch--false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.panda.thread.stop.interrupt.InterruptThreadinSleep.run(InterruptThreadinSleep.java:9)