線程的阻塞


     

 所謂的阻塞,就是線程能夠運行,但是某個條件阻止它的運行,當線程處於阻塞狀態時,調度器將忽略線程,不會分配給線程任何CPU時間,直到線程重新進入就緒狀態,它才有可能執行操作。就緒並代表是在運行啊,所謂的就緒,就是可運行也可不運行,只要調度器分配時間片給線程,線程就可以運行,因為我們都知道,調度器是如何分配線程,是不確定的。為什么任務會進入阻塞的狀態,一般有以下幾個原因:
        1.通過調用sleep(milliseconds)使任務進入休眠狀態,在這種情況下,任務在指定的時間內不會運行;
        2.通過調用wait()使線程掛起,直到線程得到了notify()或notifyAll()消息(或者java SE5的java.util.concurrent類庫中等價的signal()或signalAll()),線程才會進入就緒狀態;
        3.任務在等到某個輸入或輸出完成;
        4.任務試圖在某個對象上調用其同步控制方法,但是對象鎖不可用,因為另一個任務已經獲取這個鎖;
        阻塞的任務是必須終止的,我們不能等待其到達代碼中可以檢查其狀態值的某一點,因而決定讓它主動終止,那么就必須強制這個任務跳出阻塞狀態。那么怎樣使任務跳出阻塞狀態?就是中斷。中斷?沒錯,就是中斷,但是中斷是會拋出異常的啊!沒問題,只要不拋出就行!Thread.interrupted()提供了離開run()而不拋出異常的方式。為了調用interrupt(),我們就必須持有Thread對象,但是現在java的concurrent類庫在避免對Thread對象的直接操作,盡量通過Executor來執行所有操作。如果我們在Executor上調用shutdownNow(),那么它 將發送一個interrupt()調用給它啟動的所有線程。為什么呢?因為當我們完成工程中的某個部分或者整個程序時,通常會希望同時關閉某個特定Executor的所有任務。但是我們也想要只中斷某個單一的任務,於是就通過調用submit()而不是executor()來啟動任務,就可以持有任務的上下文。submit()將返回一個Future<?>,其中有一個未修飾的參數。為什么要用這個泛型的<?>?其實我也對這個感到興趣,但是一時半會還是理解不了,所以先放一邊吧。持有這種Future的關鍵在於可以在其上調用cancel(),因此可以使用它來中斷某個特定的任務,如果我們將true傳遞給cancel(),那么就會擁有在該線程上調用interrupt()以停止這個線程的權限。所以,cancel()是一種中斷由Executor啟動的單個線程的方式。如:
       Future<?> f = exec.submit(RunnableClass);
       f.cancel(true);
       但是我們要注意,不是所有阻塞都可以被中斷的,第一種情況可以被中斷,但是第3和第4種是不可以被中斷的,至於第2種有待研究。
       第3種的解決方案就是利用各種nio類來使阻塞的nio通道自動響應中斷。對於第4種,使用顯示的ReentrantLock對象,因為在它上面阻塞的任務是具備可以被中斷的能力,這與在synchronized方法或臨界區上阻塞的任務完全不同。這時我們可以直接調用interrupt()來中斷被阻塞的任務,如:lock.interrupt();
       當我們在線程上調用interrupt()時,中斷發生的唯一時刻是在任務要進入到阻塞操作中,或者已經在阻塞操作內部時,但是如果根據程序運行的環境,我們可能已經編寫了可能會產生這種阻塞調用的代碼,那么該怎么辦?如果我們只能通過調用在阻塞調用上拋出異常來退出,那么我們就無法總是可以離開run()循環。因此如果調用interrupt()已停止某個任務,那么在run()循環碰巧沒有產生任何阻塞調用的情況下,我們的任務就需要第二種方式來退出。於是我們就需要利用中斷狀態。中斷狀態是可以通過調用interrupted()來檢查中斷狀態,這不僅可以告訴我們interrupt()是否被調用過,而且還可以清除中斷狀態。清除中斷狀態可以確保並發結構不會就某個任務被中斷這個問題通知我們兩次,我們可以經由單一的InterruptedException或單一的成功的Thread.interrupted()測試來得到這種通知。如果想要再次檢查以了解是否被中斷,則可以在調用Thread.interrupted()時將結果存儲起來。接下來就是展示檢查的慣用法,應該在run()方法中使用它來處理在中斷狀態被設置時,被阻塞和不被阻塞的各種可能,如:
      public void run(){
           try{
                while(!Thread.interrupted()){
                         ....
                         try{
                                //被阻塞的可能
                              try{
                                    //不被阻塞的可能
                              }finally{ ...cleanup();}
                         }finally{.....cleanup();}
                  }
             }catch(InterruptedException e){}
      }
這里的慣用法特別強調,在我們經由異常離開循環時,必須要正確的清理資源。被設計用來響應interrupt()的類必須建立一種策略,來確保它將保持一致的狀態,這通常意味着所有需要清理的對象創建的操作的后面,都必須緊跟try-finally子句,從而使得無論run()循環如何退出,清理都會發生。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM