天天看點

深入了解ThreadPoolExecutor中execute()方法原理

前言

為什麼要使用線程池

1.在Java中,如果每個請求到達就建立一個新線程,開銷是相當大的。在實際使用中,伺服器在建立和銷毀線程上花費的時間和消耗的系統資源都相當大,甚至可能要比在處理實際的使用者請求的時間和資源要多的多。

2.除了建立和銷毀線程的開銷之外,活動的線程也需要消耗系統資源。如果在一個jvm裡建立太多的線程,可能會是系統由于過度消耗記憶體或“切換過度”而導緻系統資源不足。

3.為了防止資源不足,伺服器應用程式需要采取一些辦法來限制任何給定時刻處理的請求數目,盡可能減少建立和銷毀的線程的次數,特别是一個些資源耗費比較大但是線程的建立和銷毀盡量利用已有對象來進行服務,這就是“池化資源”技術産生的原因。

線程池主要解決的問題

線程生命周期開銷問題和資源不足問題(通過對多個任務重複使用線程,線程建立的開銷就被分攤到了多個任務上來,而且由于在請求到達時線程已經存在,是以消除了線程建立所帶來的延遲,這樣,就可以立即為請求服務,使應用程響應更快。另外,通過适當的調整線程中的線程數目可以防止出現資源不足的情況)

線程池的組成部分:

    一個比較簡單的線程池至少應該包含線程池管理器、工作線程、任務隊列、任務接口等部分。

    作用:

    線程池管理器 (ThreadPool):建立、銷毀并管理線程池,将工作線程放入線程池中

    工作線程 (PoolWorker):一個可以循環執行任務的線程,在沒有任務時進行等待

    任務隊列 (taskQueue):提供一種緩沖機制,将沒有處理的任務放在任務隊列中

    任務接口 (Task):每個任務必須實作的接口,主要用來規定任務的入口,任務執行完後的收尾工作,任務的執行裝填等,工作線程工通過該接口排程任務的執行。

了解ThreadPoolExecutor中execute()方法原理

首先普及一下什麼使ctl

ctl位操作變量

ThreadPoolExecutor有一個AtomicInteger變量,叫ctl(control的簡寫),一共32位,

高3位為線程池的狀态runstatus(5中:Running,Shutdown,Stop,Tidying,Terminate),

低29位存目前有效線程數workerCount

深入了解ThreadPoolExecutor中execute()方法原理

分析execute()方法的原理 

下面是execute的源碼

public void execute(Runnable command) {

    if (command == null)

        throw new NullPointerException();

    int c = ctl.get();

//判斷工作線程數小于核心線程數

    if (workerCountOf(c) < corePoolSize) {

        //執行addworker,建立一個核心線程,建立失敗重新擷取ctl

        if (addWorker(command, true))

            return;

        c = ctl.get();

    }

//如果工作線程數大于核心線程數,判斷線程池的狀态是否為running,并且可以添加進隊列

//如果線程池不是running狀态,則執行拒絕政策,(還是會調用一次addworker)

    if (isRunning(c) && workQueue.offer(command)) {

        //再次擷取ctl,進行雙重檢索

        int recheck = ctl.get();

        //如果線程池是不是處于RUNNING的狀态,那麼就會将任務從隊列中移除, 

        //如果移除失敗,則會判斷工作線程是否為0 ,如果過為0 就建立一個非核心線程 

        //如果移除成功,就執行拒絕政策,因為線程池已經不可用了;

        if (! isRunning(recheck) && remove(command))

            reject(command);

        else if (workerCountOf(recheck) == 0)

            addWorker(null, false);

    }

    //線程池挂了或者大于最大線程數

    else if (!addWorker(command, false))

        reject(command);

}