1. 程式人生 > >Java執行緒詳解

Java執行緒詳解

這篇文章計劃講一下整個Java執行緒的生命週期。
要了解一個執行緒,首先要從它的建立說起,然後是執行緒的執行以及執行緒與執行緒之間的互動,最後是執行緒的銷燬。
一、執行緒的建立
執行緒的建立有四種方式:
1)繼承Thread類建立執行緒
2)實現Runnable介面建立執行緒
3)使用Callable和Future建立執行緒
4)使用執行緒池例如用Executor框架
具體參見部落格:執行緒建立的四種方式
二、執行緒的執行
執行緒的執行時狀態一共有三種:就緒態、阻塞態和執行態,它們之間的狀態轉換關係如下圖所示:
執行緒的執行狀態
詳細介紹參見部落格:執行緒的生命週期
執行緒之間的互動參見以下博文:

執行緒基礎 第一篇:執行緒的定義、狀態、屬性、簡單實現執行緒
執行緒基礎 第二篇:多執行緒之間的同步
執行緒基礎 第三篇:多執行緒之間的通訊
JAVA多執行緒之執行緒間的通訊方式
三、執行緒的銷燬
執行緒屬於一次性消耗品,在執行完run()方法之後執行緒便會正常結束了,執行緒結束後便會銷燬,不能再次start,只能重新建立新的執行緒物件,但有時run()方法是永遠不會結束的。例如在程式中使用執行緒進行Socket監聽請求,或是其他的需要迴圈處理的任務。在這種情況下,一般是將這些任務放在一個迴圈中,如while迴圈。當需要結束執行緒時,如何退出執行緒呢?
參見博文:Java結束執行緒的三種方法
Java垃圾回收無效執行緒嗎?
(這篇文章說的有點問題,執行緒不是由Java垃圾回收來銷燬的,Java垃圾回收回收的是Java物件,而執行緒是一種系統資源,是由Java虛擬機器負責釋放的,關於Java垃圾回收,見下面一篇博文)
java多執行緒與併發(六)——java的垃圾回收機制
Java的結構之美【2】——銷燬物件
四、引入執行緒池
在web開發中,伺服器需要接受並處理請求,所以會為一個請求來分配一個執行緒來進行處理。如果每次請求都新建立一個執行緒的話實現起來非常簡便,但是存在一個問題:如果併發的請求數量非常多,但每個執行緒執行的時間很短,這樣就會頻繁的建立和銷燬執行緒,如此一來會大大降低系統的效率。可能出現伺服器在為每個請求建立新執行緒和銷燬執行緒上花費的時間和消耗的系統資源要比處理實際的使用者請求的時間和資源更多。
那麼有沒有一種辦法使執行完一個任務,並不被銷燬,而是可以繼續執行其他的任務呢?
參見博文:
深入理解Java執行緒池:ThreadPoolExecutor

淺談Java併發程式設計系列(六) —— 執行緒池的使用
五、問題
1、假如你只對執行緒和執行緒池有初步的瞭解,那麼你認為執行緒池應該怎麼使用呢?
比較符合直覺的執行緒池的使用方式應該是:1. 定義一個實現了Runnable的物件作為我們要執行的任務 2. 從執行緒池中取出一個執行緒物件 3. 用取出的執行緒物件來執行我們定義的任務 4.任務執行完畢後呼叫執行緒物件的close方法把執行緒物件放回執行緒池。
上面的步驟幾乎就是我們在使用資料庫連線池時的步驟,那麼這個步驟對於執行緒池來說合適嗎?
問題出在第2、3、4步,假如我們能夠取得一個執行緒物件,那麼我們能讓這個執行緒物件執行我們定義的任務嗎?通過上面的介紹,這種方式是不可行的。因為任務是線上程建立的時候指定的,當執行緒建立以後我們就不能修改執行緒執行的任務了。那麼我們的執行緒池應該怎麼實現呢?我們不能用這種主動的方式,而應該用被動的方式,把任務提交給執行緒池,讓執行緒池為我們執行這個任務。這其中用到了生產者消費者模式。
參見博文:生產者消費者模式-Java實現
我們只負責任務的建立,而任務的消費交由執行緒池。我們是生產者,而執行緒池是消費者。
關於Java中執行緒池的詳細設計,請參見博文:JAVA執行緒池原理詳解一
淺談Java併發程式設計系列(六) —— 執行緒池的使用
深入理解Java執行緒池:ThreadPoolExecutor
2、執行緒一旦建立並執行,如果執行緒中的程式碼丟擲了異常應該如何處理?
參見博文:JAVA多執行緒之UncaughtExceptionHandler——處理非正常的執行緒中止
既然Java執行緒能設定統一的異常處理器,那麼整個Java應用能不能捕獲到未處理異常進行統一處理呢?
參見博文:java統一異常處理