1. 程式人生 > >Java併發程式設計之三:執行緒掛起、恢復與終止的正確方法

Java併發程式設計之三:執行緒掛起、恢復與終止的正確方法

出處:http://blog.csdn.NET/ns_code/article/details/17095733

掛起和恢復執行緒

Thread 的API中包含兩個被淘汰的方法,它們用於臨時掛起和重啟某個執行緒,這些方法已經被淘汰,因為它們是不安全的,不穩定的。如果在不合適的時候掛起執行緒(比如,鎖定共享資源時),此時便可能會發生死鎖條件——其他執行緒在等待該執行緒釋放鎖,但該執行緒卻被掛起了,便會發生死鎖。另外,在長時間計算期間掛起執行緒也可能導致問題。

    下面的程式碼演示了通過休眠來延緩執行,模擬長時間執行的情況,使執行緒更可能在不適當的時候被掛起:

  1. publicclass DeprecatedSuspendResume 
    extends Object implements Runnable{  
  2.     //volatile關鍵字,表示該變數可能在被一個執行緒使用的同時,被另一個執行緒修改
  3.     privatevolatileint firstVal;  
  4.     privatevolatileint secondVal;  
  5.     //判斷二者是否相等
  6.     publicboolean areValuesEqual(){  
  7.         return ( firstVal == secondVal);  
  8.     }  
  9.     publicvoid run() {  
  10.         try{  
  11.             firstVal = 0
    ;  
  12.             secondVal = 0;  
  13.             workMethod();  
  14.         }catch(InterruptedException x){  
  15.             System.out.println("interrupted while in workMethod()");  
  16.         }  
  17.     }  
  18.     privatevoid workMethod() throws InterruptedException {  
  19.         int val = 1;  
  20.         while (true){  
  21.             stepOne(val);  
  22.             stepTwo(val);  
  23.             val++;  
  24.             Thread.sleep(200);  //再次迴圈錢休眠200毫秒
  25.         }  
  26.     }  
  27.     //賦值後,休眠300毫秒,從而使執行緒有機會在stepOne操作和stepTwo操作之間被掛起
  28.     privatevoid stepOne(int newVal) throws InterruptedException{  
  29.         firstVal = newVal;  
  30.         Thread.sleep(300);  //模擬長時間執行的情況
  31.     }  
  32.     privatevoid stepTwo(int newVal){  
  33.         secondVal = newVal;  
  34.     }  
  35.     publicstaticvoid main(String[] args){  
  36.         DeprecatedSuspendResume dsr = new DeprecatedSuspendResume();  
  37.         Thread t = new Thread(dsr);  
  38.         t.start();  
  39.         //休眠1秒,讓其他執行緒有機會獲得執行
  40.         try {  
  41.             Thread.sleep(1000);}   
  42.         catch(InterruptedException x){}  
  43.         for (int i = 0; i < 10; i++){  
  44.             //掛起執行緒
  45.             t.suspend();  
  46.             System.out.println("dsr.areValuesEqual()=" + dsr.areValuesEqual());  
  47.             //恢復執行緒
  48.             t.resume();  
  49.             try{   
  50.                 //執行緒隨機休眠0~2秒
  51.                 Thread.sleep((long)(Math.random()*2000.0));  
  52.             }catch(InterruptedException x){  
  53.                 //略
  54.             }  
  55.         }  
  56.         System.exit(0); //中斷應用程式
  57.     }  
  58. }  
 某次執行結果如下:

  

    從areValuesEqual()返回的值有時為true,有時為false。以上程式碼中,在設定firstVal之後,但在設定secondVal之前,掛起新執行緒會產生麻煩,此時輸出的結果會為false(情況1),這段時間不適宜掛起執行緒,但因為執行緒不能控制何時呼叫它的suspend方法,所以這種情況是不可避免的。

    當然,即使執行緒不被掛起(註釋掉掛起和恢復執行緒的兩行程式碼),如果在main執行緒中執行asr.areValuesEqual()進行比較時,恰逢stepOne操作執行完,而stepTwo操作還沒執行,那麼得到的結果同樣可能是false(情況2)

     下面我們給出不用上述兩個方法來實現執行緒掛起和恢復的策略——設定標誌位。通過該方法實現執行緒的掛起和恢復有一個很好的地方,就是可以線上程的指定位置實現執行緒的掛起和恢復,而不用擔心其不確定性。  

對於上述程式碼的改進程式碼如下:

  1. publicclass AlternateSuspendResume extends Object implements Runnable {  
  2.     privatevolatileint firstVal;  
  3.     privatevolatileint secondVal;  
  4.     //增加標誌位,用來實現執行緒的掛起和恢復
  5.     privatevolatileboolean suspended;  
  6.     publicboolean areValuesEqual() {  
  7.         return ( firstVal == secondVal );  
  8.     }  
  9.     publicvoid run() {  
  10.         try {  
  11.             suspended = false;  
  12.             firstVal = 0;  
  13.             secondVal = 0;  
  14.             workMethod();  
  15.         } catch ( InterruptedException x ) {  
  16.             System.out.println("interrupted while in workMethod()");  
  17.         }  
  18.     }  
  19.     privatevoid workMethod() throws InterruptedException {  
  20.         int val = 1;  
  21.         while ( true ) {  
  22.             //僅當賢臣掛起時,才執行這行程式碼
  23.             waitWhileSuspended();   
  24.             stepOne(val);  
  25.             stepTwo(val);  
  26.             val++;  
  27.             //僅當執行緒掛起時,才執行這行程式碼
  28.             waitWhileSuspended();   
  29.             Thread.sleep(200);    
  30.         }  
  31.     }  
  32.     privatevoid stepOne(int newVal)   
  33.                     throws InterruptedException {  
  34.         firstVal = newVal;  
  35.         Thread.sleep(300);    
  36.     }  
  37.     privatevoid stepTwo(int newVal) {  
  38.         secondVal = newVal;  
  39.     }  
  40.     publicvoid suspendRequest() {  
  41.         suspended = true;  
  42.     }  
  43.     publicvoid resumeRequest() {  
  44.         suspended = false;  
  45.     }  
  46.     privatevoid waitWhileSuspended()   
  47.                 throws InterruptedException {  
  48.         //這是一個“繁忙等待”技術的示例。
  49.         //它是非等待條件改變的最佳途徑,因為它會不斷請求處理器週期地執行檢查, 
  50.         //更佳的技術是:使用Java的內建“通知-等待”機制
  51.         while ( suspended ) {  
  52.             Thread.sleep(200);  
  53.         }  
  54.     }  
  55.     publicstaticvoid main(String[] args) {  
  56.         AlternateSuspendResume asr =   
  57.                 new AlternateSuspendResume();  
  58.         Thread t = new Thread(asr);  
  59.         t.start();  
  60.         //休眠1秒,讓其他執行緒有機會獲得執行
  61.         try { Thread.sleep(1000); }   
  62.         catch ( InterruptedException x ) { }  
  63.         for ( int i = 0; i < 10; i++ ) {  
  64.             asr.suspendRequest();  
  65.             //讓執行緒有機會注意到掛起請求
  66.             //注意:這裡休眠時間一定要大於
  67.             //stepOne操作對firstVal賦值後的休眠時間,即300ms,
  68.             //目的是為了防止在執行asr.areValuesEqual()進行比較時,
  69.             //恰逢stepOne操作執行完,而stepTwo操作還沒執行
  70.             try { Thread.sleep(350); }   
  71.             catch ( InterruptedException x ) { }  
  72.             System.out.println("dsr.areValuesEqual()=" +   
  73.                     asr.areValuesEqual());  
  74.             asr.resumeRequest();  
  75.             try {   
  76.