[并发编程] - Executor框架#ThreadPoolExecutor源码解读01
文章目錄
- Pre
- Thread
- Java線程與OS線程
- 生命狀態
- 狀態切換
- 線程池
- why
- use case
- Advantage
- Executor框架
- ThreadPoolExecutor 源碼分析
- 高三位低29位
- ctl相關方法
- 線程池存在5種狀態
Pre
Java-Java中的線程池原理分析及使用
Thread
線程是調度CPU資源的最小單位,線程模型分為KLT模型與ULT模型。
Java線程與OS線程
JVM使用的KLT模型,Java線程與OS線程保持1:1的映射關系,也就是說有一個java線程也會在操作系統里有一個對應的線程 。
[并發編程] - 操作系統底層工作原理
使用new Thread 創建500個線程
public static void main(String[] args) {for (int i = 0; i < 500; i++) {new Thread(() -> {while (true){try {TimeUnit.MILLISECONDS.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}}觀察OS的線程數量的增長情況 。
,停止后,再觀察其回落的狀況
驗證了 JVM使用的KLT模型,Java線程與OS線程保持1:1的映射關系。
生命狀態
- NEW 新建
- RUNNABLE 運行
- BLOCKED 阻塞
- WAITING 等待
- TIMED_WAITING 超時等待
- TERMINATED 終結
狀態切換
線程池
why
[并發編程] - 操作系統底層工作原理 中 【CPU運行安全等級】部分中說明了從用戶態切換到內核態實際上是一個非常重型的操作。
如果并發的請求數量非常多,但每個線程執行的時間很短,這樣就會頻繁的創建和銷毀線程,如此一來會大大降低系統的效率 ,可能出現服務器在為每個請求創建新線程和銷毀線程上花費的時間和消耗的系統資源要比處理實際的用戶請求的時間和資源更多。
線程池為線程生命周期的開銷和資源不足問題提供了解決方案。通過對多個任務重用線程,線程創建的開銷被分攤到了多個任務上。
use case
- 單個任務處理時間比較短
- 需要處理的任務數量很大
Advantage
- 重用存在的線程,減少線程創建,消亡的開銷,提高性能
- 提高響應速度。當任務到達時,任務可以不需要的等到線程創建就能立即執行
- 提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會消耗系統資
源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控
Executor框架
Executor接口是線程池框架中最基礎的部分,定義了一個用于執行Runnable的execute方法。
比較常見的幾個類的關系如下
Executor接口定義了唯一的接口方法
void execute(Runnable command);Executor下有一個重要子接口ExecutorService,其中定義了線程池的具體行為
ExecutorService extends Executor- submit(task):可用來提交Callable或Runnable任務,并返回代表此任務的Future
對象 - shutdown():在完成已提交的任務后封閉辦事,不再接管新任務,
- shutdownNow():停止所有正在履行的任務并封閉辦事。
- isTerminated():測試是否所有任務都履行完畢了。
- isShutdown():測試是否該ExecutorService已被關閉。
- …
ThreadPoolExecutor 源碼分析
ThreadPoolExecutor 繼承 AbstractExecutorService ,而 AbstractExecutorService 實現了ExecutorService 接口。
高三位低29位
在Java中,一個int占據32位, 使用了Integer類型來保存,高3位保存runState,低29位保存workerCount
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));// 用多少二進制位表示線程數量private static final int COUNT_BITS = Integer.SIZE - 3;// 線程最大數量 COUNT_BITS 就是29,CAPACITY就是1左移29位減1(29個1) 約5億private static final int CAPACITY = (1 << COUNT_BITS) - 1;先看下這個ctl , 通過 ctlOf(RUNNING, 0) 可以知道 ctlOf這個方法中包含的兩個參數信息 : 線程池的運行狀態 runState + 線程池中有效線程的數量 workerCount 。
COUNT_BITS : 29
// 高三位表示 線程池狀態 // runState is stored in the high-order bits private static final int RUNNING = -1 << COUNT_BITS;RUNNING 表示線程池處于 運行狀態,COUNT_BITS 是 29,因此這個位運算就表示 -1 左移 29 位。
-1 如用 2 進制表示
獲取 -1 的正數,也就是 1 的二進制: 0000000…00000 1 (前面 31 位 0)
對上一步進行取反, 1111111111…1111 0 (前面 31 位 1)
對上一步 +1 操作, 111111111…1111 (32 位 1)
因此 - 1 左移 29 位, 就得到了 111 0000…00000 ( 29個 0) 。 高三位 111 表示 RUNNING 狀態
同理
// 高三位為 000 private static final int SHUTDOWN = 0 << COUNT_BITS; // 高三位為 001 private static final int STOP = 1 << COUNT_BITS; // 高三位為 010 private static final int TIDYING = 2 << COUNT_BITS; // 高三位為 011 private static final int TERMINATED = 3 << COUNT_BITS;ctl相關方法
// Packing and unpacking ctl// 獲取運行狀態private static int runStateOf(int c) { return c & ~CAPACITY; }// 獲取活動線程數private static int workerCountOf(int c) { return c & CAPACITY; }// 獲取運行狀態和活動線程數的值private static int ctlOf(int rs, int wc) { return rs | wc; }線程池存在5種狀態
-
RUNNING
線程池處在RUNNING狀態時,能夠接收新任務,以及對已添加的任務進行處理。
線程池的初始化狀態是RUNNING。換句話說,線程池被一旦被創建,就處于RUNNING狀態,并且線程池中的任務數為0
-
SHUTDOWN
線程池處在SHUTDOWN狀態時,不接收新任務,但能處理已添加的任務
調用線程池的shutdown()接口時,線程池由RUNNING -> SHUTDOWN
-
STOP
線程池處在STOP狀態時,不接收新任務,不處理已添加的任務,并且會中斷正在處理的任務。
調用線程池的shutdownNow()接口時,線程池由(RUNNING or SHUTDOWN ) -> STOP
-
TIDYING
當所有的任務已終止,ctl記錄的”任務數量”為0,線程池會變為TIDYING狀態。當線程池變為TIDYING狀態時,會執行鉤子函數terminated()。terminated()在ThreadPoolExecutor類中是空的,若用戶想在線程池變為TIDYING時,進行相應的處理;可以通過重載terminated()函數來實現。
當線程池在SHUTDOWN狀態下,阻塞隊列為空并且線程池中執行的任務也為空時,就會由 SHUTDOWN -> TIDYING。 當線程池在STOP狀態下,線程池中執行的任務為空時,就會由STOP -> TIDYING。
- TERMINATED
線程池徹底終止,就變成TERMINATED狀態。
線程池處在TIDYING狀態時,執行完terminated()之后,就會由 TIDYING > TERMINATED。
進入TERMINATED的條件如下:
- 線程池不是RUNNING狀態;
- 線程池狀態不是TIDYING狀態或TERMINATED狀態;
- 如果線程池狀態是SHUTDOWN并且workerQueue為空;
- workerCount為0;
- 設置TIDYING狀態成功
總結
以上是生活随笔為你收集整理的[并发编程] - Executor框架#ThreadPoolExecutor源码解读01的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [并发编程] - 操作系统底层工作原理
- 下一篇: [并发编程] - Executor框架#