在Thread類中提供了能夠中止線程的方法(包括殺死和掛起):html
@Deprecated
public final void stop(){}
@Deprecated
public final void suspend(){}
stop 和 suspend 添加的有Deprecated註釋,也便是該方法已經廢棄使用。那麼爲何會不建議使用這兩種方法呢?還有沒有其餘中止線程的方法?安全
一、stop()會當即殺死線程,沒法保證原子操做可以順利完成,存在數據錯誤的風險。不管線程執行到哪裏、是否加鎖,stop會當即中止線程運行,直接退出。多線程
以下代碼:ide
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(); } }
運行結果以下:post
account01: 3
account02: 6
很明顯沒有保證二者的和爲10。說明在線程循環過程當中,最後一個加鎖循環體沒有完整執行結束,數據發生錯誤。除此以外,在執行stop方法以後,線程會當即釋放鎖,這一樣也會致使原子操做失敗數據異常。url
官方註釋:spa
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.
二、suspend()並未殺死線程,只是把線程掛起,中止線程的運行。但掛起以後並不會釋放鎖,這樣,若是有其它多個線程在等待該鎖,則程序將會發生死鎖。線程
以下代碼:code
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()); } }
運行結果以下: htm
.....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嚴格意義上說並不屬於中止線程的方法。
那麼,到底該如何安全的中止線程呢?
遵循的規則:讓線程本身中止本身。
兩種方法:一、線程任務執行完成,順利結束退出。二、設置終止標誌位,在循環的時候進行終止標誌位檢測,若是設置爲終止狀態則return結束線程。
例如:一、線程執行完成自動退出:
public void run() { for(int i = 0;i<10;i++){//循環十次以後run方法結束自動結束線程 System.out.println(Thread.currentThread().getName()); } }
二、設置終止標誌位。
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