個人博客
ThreadPoolExecutor 工作線程Worker自身鎖設計 | iwts’s blog
總集
想要完整了解下ThreadPoolExecutor?可以參考:
基于源碼詳解ThreadPoolExecutor實現原理 | iwts’s blog
Worker-工作線程管理
線程池設計了內部類Worker
,主要是用來管理新建的線程,除了監控,核心的方法是:
- 執行。
- 申請任務。
此外還包括回收等線程監控類型方法。
由于一個工作線程對象,其中有一個具體的線程,那么本質上是不需要加鎖的。競爭資源是任務隊列,而任務隊列由阻塞隊列來實現。
可以看Worker的設計:
工作線程自身鎖
Worker對象其實本身就是一把鎖。這是個細節,Worker本身是實現了AQS的:
這里其實最主要的作用是工作線程的回收。雖然可以通過維護workers來完成對工作線程生命周期的管理,新建線程比較好理解,但是刪除線程的時候,工作線程本身就是一種競爭資源了。回收的時候是可能恰好碰到調用的。
這里選擇AQS的原因,其實可以看注釋,這邊簡單翻譯一部分:
Worker類存在的主要意義就是為了維護線程的中斷狀態。因為正在執行任務的線程是不應該被中斷的。在線程真正開始運行任務之前,為了抑制中斷。所以把 Worker 的狀態初始化為負數-1。
完全看不懂,這里從其他角度慢慢繞過來解釋一下。
線程的中斷與回收與加鎖時機
解釋這個問題,首先看下Worker自身是從哪里調用鎖的:
- 工作線程處理前后加鎖。
- 工作線程嘗試中斷時嘗試獲鎖。
第一個看代碼runWorker()
:
也就是說,當前線程如果在處理,那么本身是給自己加鎖的。
第二個看代碼interruptIdleWorkers()
:
這里是不是就有點恍然大悟的意思了。
工作線程本身實現AQS,將自身當作競爭對象。
那么工作線程工作的時候,加鎖,鎖住自己,那么interruptIdleWorkers()
方法在執行的時候,如果能獲取鎖,就說明一個問題:此時當前線程是沒有在工作的。那么就會被中斷掉。
為了實現這個功能,就只能選擇不可重入鎖,所以自己實現了AQS來實現這個特性。具體可以看代碼實現。
不使用ReentrantLock和synchronized的原因
為啥要加鎖,就是為了區分線程是否中斷,而ReentrantLock和synchronized都有一個重要特性:可重入。因為可重入,那么這個鎖就沒有意義了,因為線程都是一個,既然可重入那么就是必然能獲鎖了。
所以選用AQS,手動刪掉可重入的特性,實現互斥。
tryAcquire實現不可重入
也可以看看重寫AQS的獲鎖代碼:
雖然設置的excluiveOwnerThread,但是完全不用,就是直接CAS獲鎖,沒有重入的特性。