多線程(四) 如何停止線程
在Thread類中提供了可以停止線程的方法(包括殺死和掛起):
[email protected]
public final void stop(){}
@Deprecated
public final void suspend(){}
stop 和 suspend 添加的有Deprecated註釋,也即是該方法已經廢棄使用。那麽為什麽會不建議使用這兩種方法呢?還有沒有其他停止線程的方法?
1、stop()會立即殺死線程,無法保證原子操作能夠順利完成,存在數據錯誤的風險。無論線程執行到哪裏、是否加鎖,stop會立即停止線程運行,直接退出。
如下代碼:
int account01 = 10; int account02= 0; Object lock = new Object(); public void testStop() { class StopRunnable implements Runnable { @Override public void run() { //要求 account01 + account02 =10 恒成立 while (true) {synchronized (lock) {//加鎖保證操作的原子性 account01--; sleep(200);//睡眠模擬執行過程 account02++; } } } } Thread thread = new Thread(new StopRunnable()); thread.start(); sleep(1300);thread.stop(); System.out.println("account01: " + account01 + "\naccount02: " + account02); }
private void sleep(int time){
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
運行結果如下:
account01: 3
account02: 6
很明顯沒有保證兩者的和為10。說明在線程循環過程中,最後一個加鎖循環體沒有完整執行結束,數據發生錯誤。除此之外,在執行stop方法之後,線程會立即釋放鎖,這同樣也會導致原子操作失敗數據異常。
官方註釋:
Forces the thread to stop executing.
It is permitted to stop a thread that has not yet been started.
If the thread is eventually started, it immediately terminates.
2、suspend()並未殺死線程,只是把線程掛起,停止線程的運行。但掛起之後並不會釋放鎖,這樣,如果有其它多個線程在等待該鎖,則程序將會發生死鎖。
如下代碼:
int account01 = 10; int account02= 0; Object lock = new Object(); public void testStop() { class StopRunnable implements Runnable { @Override public void run() { //要求 account01 + account02 =10 恒成立 for(int i =0;i<5;i++){ synchronized (lock) {//加鎖保證操作的原子性 account01--; System.out.println("....."+Thread.currentThread().getName());//為了看到線程停止添加輸出線程名稱操作 sleep(200);//睡眠200ms account02++; } } } } Thread thread01 = new Thread(new StopRunnable()); thread01.setName("thread01"); Thread thread02 = new Thread(new StopRunnable()); thread02.setName("thread02"); thread01.start(); thread02.start(); sleep(500); thread01.suspend(); while (true){ sleep(1000); System.out.println("account01: " + account01 + " account02: " + account02+" thread01 isAlive:"+thread01.isAlive()+" thread02 isAlive:"+thread02.isAlive()); } }
運行結果如下:
.....thread01
.....thread01
.....thread01
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true
由結果可以看出,thread01一直在運行,而thread02一次也沒有執行到run方法。然後在執行thread01.suspend()之後,兩個線程都停止了運行run方法。但同時兩個線程都沒有死亡。
在代碼中只對thread01執行了suspend,那麽如果thread02獲取到鎖則應該繼續由thread02執行run方法,但並沒有,說明鎖lock一直由thread01持有,在掛起之後並未釋放。
其實在使用suspend()方法的時候是需要配合resume()同時使用的。
....
....
sleep(500); thread01.suspend(); int time = 0;//添加time計數,在循環三次之後釋放 while (true){ time ++; if(time ==3){ thread01.resume();//釋放線程 } sleep(1000); System.out.println("account01: " + account01 + " account02: " + account02+" thread01 isAlive:"+thread01.isAlive()+" thread02 isAlive:"+thread02.isAlive()); }
執行結果如下:
.....thread01 .....thread01 .....thread01 account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true account01: 7 account02: 2 thread01 isAlive:true thread02 isAlive:true .....thread01 //釋放之後繼續運行 .....thread01 .....thread02 //thread01釋放鎖,thread02獲取鎖繼續運行 .....thread02 .....thread02 account01: 2 account02: 7 thread01 isAlive:false thread02 isAlive:true //thread01 死亡,thread02活著 .....thread02 .....thread02 account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false account01: 0 account02: 10 thread01 isAlive:false thread02 isAlive:false
可以看出,thread01.resume()之後thread01繼續運行,然後運行結束釋放鎖之後,thread02接著運行起來,這時可以看到thread01已經死亡,而thread02依舊活著。直至兩個線程全部結束。如果正常使用suspend()和resume()並不會出現太大問題,只是在涉及到鎖的時候久需要格外小心了。r如果沒有使用到鎖,那麽其中一個線程的掛起並不會影響到其他線程的執行。
對於public void interrupt(){}方法,該方法只是對阻塞狀態的線程(seleep、wait、join和IO/NIO操作)進行中斷操作,在調用interrupt方法的時候,如果線程處於阻塞狀態則會拋出InterruptedException/ClosedByInterruptException。在程序中只需對異常進行捕獲,並不影響後續的操作。對於未處於阻塞狀態的線程,調用interrupt方法沒有任何影響。所以interrupt嚴格意義上說並不屬於停止線程的方法。
那麽,到底該如何安全的停止線程呢?
遵循的規則:讓線程自己停止自己。
兩種方法:1、線程任務執行完成,順利結束退出。2、設置終止標誌位,在循環的時候進行終止標誌位檢測,如果設置為終止狀態則return結束線程。
例如:1、線程執行完成自動退出:
public void run() { for(int i = 0;i<10;i++){//循環十次之後run方法結束自動結束線程 System.out.println(Thread.currentThread().getName()); } }
2、設置終止標誌位。
boolean isStop = false;//終止標誌位 當需要結束線程時,更改為true public void testInterrupt(){ Thread th = new Thread(new Runnable() { @Override public void run() { while(true){ if(isStop){//當需要結束線程的時候終止線程 //doSomething 進行一些收尾工作 return; } System.out.println(Thread.currentThread().getName()); } } });
設置終止標誌位的時候線程不會立即終止,只有當循環到標誌位判斷的時候才會執行退出操作,這樣就可以在循環體中合適的地方執行退出邏輯,可以保證原子操作的順利完成以及鎖的釋放。
對於ExecutorService的void shutdown();方法,該方法只是停止線程池接受新的任務同時等待已提交線程結束,並不會停止線程。所以該方法不屬於停止線程的方法。
=========================================
原文鏈接:多線程(四) 如何停止線程 轉載請註明出處!
=========================================
---end
多線程(四) 如何停止線程