天天看點

線程池_03_源碼分析

線程池_03_源碼分析

  • 關鍵變量介紹:
    •  線程的狀态
      • 5種
        • RUNNABLE:運作狀态,接受新任務,持續處理任務隊列裡的任務
        • SHUTDOWN:不再接受新任務,但要處理任務隊列裡的任務
        • STOP:不接受新任務,不再處理任務隊列裡的任務,中斷正在進行中的任務
        • TIDYING:表示線程池正在停止運作,中止所有任務,銷毀所有工作線程
        • TERMINATED:表示線程池已停止運作,所有工作線程已被銷毀,所有任務已被清空或執行完畢
      • 狀态裝換
        線程池_03_源碼分析
      • 細節
        線程池_03_源碼分析
        • 線程的狀态使用ctl表示,預設為 running
        • private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
        • ctl類型為AtomicInteger,那用一個基礎如何表示以上五種狀态以及線程池工作線程數量呢?int型變量占用4位元組,共32位,是以采用位表示,可以解決上述問題。5種狀态使用5種數值進行表示,需要占用3位,餘下的29位就可以用來表示線程數。是以,高三位表示程序狀态,低29位為線程數量,代碼如下:
          • private staticfinal int COUNT_BITS = Integer.SIZE - 3; // 值為29
          • private staticfinal int CAPACITY   = (1 <<COUNT_BITS) - 1; //高三位全為0,低29位全為1,是以線程數量的表示範圍為 0 ~ 2^29
          • 因為ctl分位來表示狀态和數量,下面幾個狀态僅看有效位的值
            • private staticfinal int RUNNING    = -1 <<COUNT_BITS; // 有效值為 111
            • private staticfinal int SHUTDOWN   =  0 << COUNT_BITS; // 有效值為 000
            • private staticfinal int STOP       =  1 << COUNT_BITS; // 有效值為 001
            • private staticfinal int TIDYING    =  2 << COUNT_BITS; // 有效值為 010
            • private staticfinal int TERMINATED =  3 <<COUNT_BITS; // 有效值為 011
        • 采用int分位表示線程池狀态和線程數量; 并且提供runStateOf(): 擷取線程池狀态 和 workerCountOf(): 擷取工作線程數量兩個方法,均為二進制操作。
    • 工作線程(Worker)
      •  線程池中的工作線程以Worker作為展現,真正工作的線程為Worker的成員變量,Worker即是Runnable,又是同步器。Worker從工作隊列中取出任務來執行,并能通過Worker控制任務狀态。
  • 執行任務,源碼分析:
    • 1. execute() --送出線程任務;
      線程池_03_源碼分析
    • 2. addWorker()  -- 通過添加核心和非核心線程來執行任務
      • private boolean addWorker(Runnable firstTask, boolean core) {
        • int c = this.ctl.get();           // 擷取目前ctl值
        • label253:
        • while(!runStateAtLeast(c, 0) || !runStateAtLeast(c, 536870912) && firstTask == null && !this.workQueue.isEmpty()) {
        • // workerCountOf(c)-擷取線程數  <  core ? this.corePoolSize : this.maximumPoolSize 如果為true,核心線程;否則,非核心線程
          • while(workerCountOf(c) < ((core ? this.corePoolSize : this.maximumPoolSize) & 536870911)) {
          • // CAS操作增加線程數,跳出循環
          • if (this.compareAndIncrementWorkerCount(c)) {
            • boolean workerStarted = false;
            • boolean workerAdded = false;
            • ThreadPoolExecutor.Worker w = null;
            • try {
              • w = new ThreadPoolExecutor.Worker(firstTask);
              • Thread t = w.thread;
              • if (t != null) {
                • ReentrantLock mainLock = this.mainLock;
                • mainLock.lock();
                  • int c = this.ctl.get();
                    • if (isRunning(c) || runStateLessThan(c, 536870912) && firstTask == null) {
                      • if (t.isAlive()) {
                        • throw new IllegalThreadStateException();
                      • }
                      • this.workers.add(w);
                      • int s = this.workers.size();
                      • if (s > this.largestPoolSize) {
                        • this.largestPoolSize = s;
                      • workerAdded = true;
                  • } finally {
                    • mainLock.unlock();
                  • if (workerAdded) {
                    • t.start();
                    • workerStarted = true;
                • if (!workerStarted) {
                  • this.addWorkerFailed(w);
              • return workerStarted;
            • c = this.ctl.get();
            • // 上面的CAS操作沒成功,檢查線程池狀态與開始是否一緻;如果一緻,繼續執行此for循環,否則重新執行retry代碼塊;自旋以期CAS成功,後續才能添加線程
            • if (runStateAtLeast(c, 0)) {
              • continue label253;
          • return false;
      • addWorkerFailed(w)
        • 首先是将Worker移除,然後通過CAS操作更新ctl,最後調用tryTerminate()操作嘗試中止線程池。
    • 4. runWorker()
      • 線程首個任務為firstTask,之後通過getTask()就從阻塞隊列裡任務。線程池提供了beforeExecute()和afterExecute()通知子類任務執行前後的回調,讓子類有時機能執行自己的事情。如果線程池已沒有任務了,工作線程達到了可退出的狀态,則将線程退出。
      • 線程池執行的任務的線程,也就是Workder裡的Thread。是以在addWorker()中執行new ThreadPoolExecutor.Worker(firstTask)後;執行的是Worker.run(),run()則調用了ThreadPoolExecutor.runWorker()
        線程池_03_源碼分析
    • 5. getTask()
      • 線程池裡的線程從阻塞隊列裡拿任務,如果存在非核心線程,假設阻塞隊列裡沒有任務,那麼非核心線程也要在等到keepAliveTime時間後才會釋放。
      • 如果目前僅有核心線程存在,如果允許釋放核心線程的話,也就和非核線程的處理方式一樣,反之,則通過take()一直阻塞直到拿到任務,這也就是線程池裡的核心線程為什麼不死的原因。
        線程池_03_源碼分析
    • 6. processWorkerExit() 
      • 線上程沒有拿到任務後,退出線程
      • 釋放工作線程也并沒有區分核心與非核心,也是随機進行的。所謂随機,就是在前面所說的區間範圍内,根據釋放政策,哪個線程先達到擷取不到任務的狀态,就釋放哪個線程。
        線程池_03_源碼分析
      • tryTerminate()
        • 發現可以中止線程池時,中止,并調用terminated()進行通知。如果線程池處于RUNNABLE狀态,什麼也不做,否則嘗試中斷一個線程。中斷線程通過interruptIdleWorker()完成。