天天看點

Java中線程池的實作原理 初始化線程池(4種) 線程池的狀态(5種) 向線程池送出任務(2種) 線程池容量的動态調整總結:

Java中線程池的實作原理

知識點總結

-------------------------------------------------------------------------------------------------------------------

  1. 線程池的原理
  2. 線程池的五個要素
  3. 線程池的4個飽和政策
  4. 線程池的4種阻塞隊列
  5. 線程池的兩種送出方式
  6. 線程池的兩種關閉方式
  7. 線程池的動态擴容
  8. 線程池的四種類型

-------------------------------------------------------------------------------------------------------------------

jdk1.5引入Executor線程池架構,通過它把任務的送出和執行進行解耦,隻需要定義好任務,然後送出給線程池,而不用關心該任務是如何執行、被哪個線程執行,以及什麼時候執行。

初始化線程池(4種)

簡介:

Java線程池的工廠類:Executors類,

初始化4種類型的線程池:

newFixedThreadPool()

說明:初始化一個指定線程數的線程池,其中corePoolSize == maxiPoolSize,使用LinkedBlockingQuene作為阻塞隊列

特點:即使當線程池沒有可執行任務時,也不會釋放線程。

newCachedThreadPool()

說明:初始化一個可以緩存線程的線程池,預設緩存60s,線程池的線程數可達到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作為阻塞隊列;

特點:在沒有任務執行時,當線程的空閑時間超過keepAliveTime,會自動釋放線程資源;當送出新任務時,如果沒有空閑線程,則建立新線程執行任務,會導緻一定的系統開銷;

是以,使用時要注意控制并發的任務數,防止因建立大量的線程導緻而降低性能。

newSingleThreadExecutor()

說明:初始化隻有一個線程的線程池,内部使用LinkedBlockingQueue作為阻塞隊列。

特點:如果該線程異常結束,會重新建立一個新的線程繼續執行任務,唯一的線程可以保證所送出任務的順序執行

newScheduledThreadPool()

特定:初始化的線程池可以在指定的時間内周期性的執行所送出的任務,在實際的業務場景中可以使用該線程池定期的同步資料。

總結:除了newScheduledThreadPool的内部實作特殊一點之外,其它線程池内部都是基于ThreadPoolExecutor類(Executor的子類)實作的。

ThreadPoolExecutor内部具體實作:

ThreadPoolExecutor類構造器文法形式:

ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,timeUnit,workQueue,threadFactory,handle);   

方法參數:

   corePoolSize:核心線程數

   maxPoolSize:最大線程數

     keepAliveTime:線程存活時間(在corePore<*<maxPoolSize情況下有用)

     timeUnit:存活時間的時間機關

     workQueue:阻塞隊列(用來儲存等待被執行的任務)

注:關于workQueue參數的取值,JDK提供了4種阻塞隊列類型供選擇:

        ArrayBlockingQueue:基于數組結構的有界阻塞隊列,按FIFO排序任務;

        inkedBlockingQuene:基于連結清單結構的阻塞隊列,按FIFO排序任務,吞吐量通常要高于  

            SynchronousQuene:一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處于阻塞狀态,吞吐量通常要高于ArrayBlockingQuene;

        PriorityBlockingQuene:具有優先級的無界阻塞隊列;

     threadFactory:線程工廠,主要用來建立線程;

     handler:表示當拒絕處理任務時的政策,有以下四種取值

 注: 當線程池的飽和政策,當阻塞隊列滿了,且沒有空閑的工作線程,如果繼續送出任務,必須采取一種政策處理該任務,線程池提供了4種政策:

    ThreadPoolExecutor.AbortPolicy:丢棄任務并抛出RejectedExecutionException異常。

    ThreadPoolExecutor.DiscardPolicy:也是丢棄任務,但是不抛出異常。

    ThreadPoolExecutor.DiscardOldestPolicy:丢棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)

    ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務

    當然也可以根據應用場景實作RejectedExecutionHandler接口,自定義飽和政策,如記錄日志或持久化存儲不能處理的任務。

 線程池的狀态(5種)

其中AtomicInteger變量ctl的功能非常強大:利用低29位表示線程池中線程數,通過高3位表示線程池的運作狀态:

1、RUNNING:-1 << COUNT_BITS,即高3位為111,該狀态的線程池會接收新任務,并處理阻塞隊列中的任務;

2、SHUTDOWN: 0 << COUNT_BITS,即高3位為000,該狀态的線程池不會接收新任務,但會處理阻塞隊列中的任務;

3、STOP : 1 << COUNT_BITS,即高3位為001,該狀态的線程不會接收新任務,也不會處理阻塞隊列中的任務,而且會中斷正在運作的任務;

4、TIDYING : 2 << COUNT_BITS,即高3位為010,該狀态表示線程池對線程進行整理優化;

5、TERMINATED: 3 << COUNT_BITS,即高3位為011,該狀态表示線程池停止工作;

 向線程池送出任務(2種)

有兩種方式:

      Executor.execute(Runnable command);

      ExecutorService.submit(Callable<T> task);

execute()内部實作

1.首次通過workCountof()獲知目前線程池中的線程數,

  如果小于corePoolSize, 就通過addWorker()建立線程并執行該任務;

 否則,将該任務放入阻塞隊列;

2. 如果能成功将任務放入阻塞隊列中,  

如果目前線程池是非RUNNING狀态,則将該任務從阻塞隊列中移除,然後執行reject()處理該任務;

如果目前線程池處于RUNNING狀态,則需要再次檢查線程池(因為可能在上次檢查後,有線程資源被釋放),是否有空閑的線程;如果有則執行該任務;

3、如果不能将任務放入阻塞隊列中,說明阻塞隊列已滿;那麼将通過addWoker()嘗試建立一個新的線程去執行這個任務;如果addWoker()執行失敗,說明線程池中線程數達到maxPoolSize,則執行reject()處理任務;

 sumbit()内部實作

會将送出的Callable任務會被封裝成了一個FutureTask對象

FutureTask類實作了Runnable接口,這樣就可以通過Executor.execute()送出FutureTask到線程池中等待被執行,最終執行的是FutureTask的run方法; 

比較:

 兩個方法都可以向線程池送出任務,execute()方法的傳回類型是void,它定義在Executor接口中, 而submit()方法可以傳回持有計算結果的Future對象,它定義在ExecutorService接口中,它擴充了Executor接口,其它線程池類像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有這些方法。 

線程池的關閉(2種)

  ThreadPoolExecutor提供了兩個方法,用于線程池的關閉,分别是shutdown()和shutdownNow(),其中:

    shutdown():不會立即終止線程池,而是要等所有任務緩存隊列中的任務都執行完後才終止,但再也不會接受新的任務

    shutdownNow():立即終止線程池,并嘗試打斷正在執行的任務,并且清空任務緩存隊列,傳回尚未執行的任務

 線程池容量的動态調整

  ThreadPoolExecutor提供了動态調整線程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),

總結:

線程池中的核心線程數,當送出一個任務時,線程池建立一個新線程執行任務,直到目前線程數等于corePoolSize;如果目前線程數為corePoolSize,繼續送出的任務被儲存到阻塞隊列中,等待被執行;如果阻塞隊列滿了,那就建立新的線程執行目前任務;直到線程池中的線程數達到maxPoolSize,這時再有任務來,隻能執行reject()處理該任務;

注:如果執行了線程池的prestartAllCoreThreads()方法,線程池會提前建立并啟動所有核心線程。

posted on 2019-02-28 17:30  shoshana~ 閱讀( ...) 評論( ...) 編輯 收藏

轉載于:https://www.cnblogs.com/shoshana-kong/p/10451809.html