1. 程式人生 > >多執行緒——Java執行緒池原理深入

多執行緒——Java執行緒池原理深入

      上次我們簡單瞭解了一下什麼是執行緒池以及Java中幾種型別的執行緒池,今天我們來深入剖析一下執行緒池的原理。

1、構造

      1. 執行緒池管理器(ThreadPoolManager):用於建立並管理執行緒池

      2. 工作執行緒(WorkThread): 執行緒池中執行緒

      3. 任務介面(Task):每個任務必須實現的介面,以供工作執行緒排程任務的執行。

      4. 任務佇列:用於存放沒有處理的任務。提供一種緩衝機制。

注:執行緒池管理器至少有下列功能:建立執行緒池,銷燬執行緒池,新增新任務

2、執行緒狀態

這裡寫圖片描述

       新建狀態(New):新建立了一個執行緒物件。
      就緒狀態(Runnable):執行緒物件建立後,其他執行緒呼叫了該物件的start()方法。該狀態的執行緒位於可執行執行緒池中,變得可執行,等待獲取CPU的使用權。
      執行狀態(Running):就緒狀態的執行緒獲取了CPU,執行程式程式碼。
      阻塞狀態(Blocked):阻塞狀態是執行緒因為某種原因放棄CPU使用權,暫時停止執行。直到執行緒進入就緒狀態,才有機會轉到執行狀態。阻塞的情況分三種:
      (一)、等待阻塞:執行的執行緒執行wait()方法,JVM會把該執行緒放入等待池中。
      (二)、同步阻塞:執行的執行緒在獲取物件的同步鎖時,若該同步鎖被別的執行緒佔用,則JVM會把該執行緒放入鎖池中。
      (三)、其他阻塞:執行的執行緒執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該執行緒置為阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態。
      死亡狀態(Dead):

執行緒執行完了或者因異常退出了run()方法,該執行緒結束生命週期。

3、執行流程

這裡寫圖片描述

      a.一個任務提交,如果執行緒池大小沒達到corePoolSize,則每次都啟動一個worker也就是一個執行緒來立即執行
      b.如果來不及執行,則把多餘的執行緒放到workQueue,等待已啟動的worker來迴圈執行
      c.如果佇列workQueue都放滿了還沒有執行,則在maximumPoolSize下面啟動新的worker來迴圈執行workQueue
      d.如果啟動到maximumPoolSize還有任務進來,執行緒池已達到滿負載,此時就執行任務拒絕RejectedExecutionHandler

// 流程就是:沒達到corePoolSize,建立worker執行,達到corePoolSize加入workQueue
//           workQueue滿了且在maximumPoolSize下,建立新worker,達到maximumPoolSize,執行reject
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();

    // 1:poolSize達到corePoolSize,執行3把任務加入workQueue
    // 2:poolSize沒達到,執行addIfUnderCorePoolSize()在corePoolSize內建立新worker立即執行任務
    //    如果達到corePoolSize,則同上執行3
    if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
        // 3:workQueue滿了,執行5
        if (runState == RUNNING && workQueue.offer(command)) {
            if (runState != RUNNING || poolSize == 0) {
                // 4:如果執行緒池關閉,執行拒絕策略
                //   如果poolSize==0,新啟動一個執行緒執行佇列內任務
                ensureQueuedTaskHandled(command);
            }
            // 5:在maximumPoolSize內建立新worker立即執行任務
            //   如果達到maximumPoolSize,執行6拒絕策略
        } else if (!addIfUnderMaximumPoolSize(command))
            // 6:拒絕策略
            reject(command); // is shutdown or saturated
    }
}

4、執行緒銷燬

      keepAliveTime:代表的就是執行緒空閒後多久後銷燬,執行緒的銷燬是通過worker的getTask()來實現的。
      一般來說,Worker會迴圈獲取getTask(),如果getTask()返回null則工作執行緒worker終結,那我們再看看什麼時候getTask()返回null

Runnable getTask() {
    for (;;) {
        try {
            int state = runState;
            if (state > SHUTDOWN)
                return null;
            Runnable r;
            if (state == SHUTDOWN)  // Help drain queue
                r = workQueue.poll();
            else if (poolSize > corePoolSize || allowCoreThreadTimeOut)
                // 在poolSize大於corePoolSize或允許核心執行緒超時時
                // 阻塞超時獲取有可能獲取到null,此時worker執行緒銷燬
                r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
            else
                r = workQueue.take();
            if (r != null)
                return r;
            // 這裡是是否執行worker執行緒銷燬的判斷
            if (workerCanExit()) {
                if (runState >= SHUTDOWN)
                    // STOP或TERMINATED狀態,終止空閒worker
                    interruptIdleWorkers();
                return null; // 這裡返回null,代表工作執行緒worker銷燬
            }
            // 其他:retry,繼續迴圈
        } catch (InterruptedException ie) {
            // On interruption, re-check runState
        }
    }
}

5、執行緒池關閉

      平緩關閉 shutdown:這個方法會將runState置為SHUTDOWN,會終止所有空閒的執行緒,同時不再接受新的任務,而仍在工作的執行緒不受影響,所以佇列中的任務人會被執行
      立即關閉 shutdownNow:此方法將runState置為STOP,和shutdown方法的區別是,這個方法會終止所有的執行緒(取消所有正在執行和未執行的任務),所以佇列中的任務也不會被執行了。

總結:

      通過這次學習,我們不僅知道了執行緒池是如何管理執行緒,而且還了解了執行緒多種狀態之間的轉換,這樣更加便於我們對執行緒池的理解。之後,我們還將繼續學習關於多執行緒的知識,讓我們每天都有成長。