【併發】Java線程池總結

Java線程池總結

一、什麼是線程池?

線程池(百度百科):一種線程管理模式。線程維護着多個線程,等待着分配可併發執行的任務。這避免了在處理短時間任務時創建和銷燬線程的代價,進而影響影響緩存性能。線程池不僅不僅保證內核的充分利用,還能防止過分調度。

線程池(自己總結):線程池就是實現創建若干個可執行的線程放入一個池中,需要的時候從池中獲取,不用自行創建,使用完畢不需要銷燬線程而是放入池中,從而減少了創建和銷燬線程的開銷。

線程池的意義

  1. 循環利用線程資源,避免重複創建和銷燬線程

  2. 線程池的任務是異步執行的,只要提交完成就能快速返回,可以提高應用響應性

  3. Java線程池還有一個很重要的意義:Java線程池就是JDK 5 推出的Executor框架,在此之前Java線程既是工作任務又是執行機制,而Executor框架把工作任務與執行機制分離開來:工作任務包括Runnable接口和Callable接口,而執行機制由Executor接口提供。

二、 Java中四種線程池

Java中通過工具類Executors提供的4中線程池:

1. newFixedThreadExecutor() : 創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因爲執行異常而結束,那麼線程池會補充一個新線程。

public static ExecutorService newFixedThreadPool(int nThreads) {    
    return new ThreadPoolExecutor(nThreads, nThreads, 
                                  0L, TimeUnit.MILLISECONDS, 
                                  new LinkedBlockingQueue<Runnable>());
}

2. newSingleThreadExecutor() :創建單線程化的線程池。這個線程池只有一個線程在工作,如果這個唯一的線程因爲異常停止結束,那麼會有一個新的線程來替代它。該線程池保證所有任務都是按照指定的順序(FIFO,LIFO,優先級)執行 。

public static ExecutorService newSingleThreadExecutor() {    
    return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 
				0L,TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}

3. newCachedThreadPool() :創建一個可緩存的線程池,可根據任務數動態調整當前線程數。如果線程池的大小超過了處理任務所需要的線程,那麼就會回收部分空閒(60s不執行)的線程,當任務數增加時, 此線程池又可以智能的添加新線程來處理任務。此線程池不會對線程池大小做限制,線程池大小完全依賴於操作系統(或者說JVM)能夠創建的最大線程大小。

public static ExecutorService newCachedThreadPool() {    
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,                                  new SynchronousQueue<Runnable>());
}

4. newScheduledThreadPool() : 創建一個大小無限的線程池。此線程池支持定時以及週期性執行任務的需求。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {    
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

三、線程池的使用

1. ThreadPoolExecutor類

方式一:使用ThreadPoolExecutor(不推薦直接使用)

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(5));
        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                
            }
        });

方式二:使用Executors類提供的幾個靜態方法(推薦)

ExecutorService executorService1 = Executors.newFixedThreadPool(3);
        ExecutorService executorService2 = Executors.newSingleThreadExecutor();
        ExecutorService executorService3 = Executors.newCachedThreadPool();
        ExecutorService executorService4 = Executors.newScheduledThreadPool(3);
        executorService1.execute(new Runnable() {
            @Override
            public void run() {
                
            }
        });

其實ExecutorService纔是真正的線程池接口,所以我們在通過Executors創建各種線程時,都是採用上述代碼所示的方式

四、線程池實現原理

1. 線程池的6個重要參數(需記牢)

  • corePoolSize : 核心池的大小

  • maximumPoolSize : 線程池最大線程數

  • keepAliveTime : 表示線程沒有任務執行時最多保持多久時間會終止。

  • workQueue:一個阻塞隊列,用來存儲等待執行的任務

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

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

  ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。 
  ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。 
  ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
  ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務 

2. 線程池實現原理

當將任務提交給線程池時,即調用execute()方法時,流程如下圖。

 

a、若當前運行的線程少於corePoolSize,則創建新線程來執行任務(執行這一步需要獲取全局鎖) b、若運行的線程多於或等於corePoolSize,則將任務加入BlockingQueue c、若無法將任務加入BlockingQueue,則創建新的線程來處理任務(執行這一步需要獲取全局鎖) d、若創建新線程將使當前運行的線程超出maximumPoolSize,任務將被拒絕,並調用RejectedExecutionHandler.rejectedExecution() 採取上述思路,是爲了在執行execute()時,儘可能避免獲取全局鎖 在ThreadPoolExecutor完成預熱之後(當前運行的線程數大於等於corePoolSize),幾乎所有的execute()方法調用都是執行步驟b,而步驟b不需要獲取全局鎖

參考

https://baike.baidu.com/item/%E7%BA%BF%E7%A8%8B%E6%B1%A0/4745661

https://www.jianshu.com/p/916ea29ff3b9

https://www.cnblogs.com/little-fly/p/10851776.html

https://zhuanlan.zhihu.com/p/61588020

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章