Java並發編程(二)——線程池


1、線程池工作原理

  Java線程池主要用於管理線程組及其運行狀態。其主要作用是線程復用、線程資源管理、控制操作系統的最大並發數。

  Java線程池的工作原理:JVM先根據用戶的參數創建一定數量的可運行的線程任務,並將其放入隊列中,在線程創建后啟動這些任務,如果線程數量超過了最大線程數量,則超出數量的線程排隊等候,在有任務執行完畢后,線程池調度器會發現有可用的線程,進而再次從隊列中取出任務並執行。

1.1 線程池的核心組件

  線程池主要有4個核心組件組成:

  • 線程池管理器:用於創建並管理線程池。
  • 工作線程:線程池中執行具體任務的線程。
  • 任務接口:用於定義工作線程的調度和執行策略,只有線程實現了該接口任務才能被線程池調度。
  • 任務隊列:存放待處理任務,新的任務將會不斷被加入隊列中,執行完成的任務將被從隊列中移除。

1.2 線程池的核心類

  Java線程池是通過Executor框架實現,該框架的核心類包括:Executor、Executors,ExecutorService、ThreadPoolExecutor、Callable、Future、FutureTash。

具體繼承關系如下:

  其中,ThreadPoolExecutor是構建線程的核心構造方法,該方法的定義如下:

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue){
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);  
}

  ThreadPoolExecutor構造函數的具體函數如下表:

參數 說明
corePoolSize
線程池中核心線程的數量
maximumPoolSize
線程池中最大線程的數量
keepAliveTime
當前線程數量超過corePoolSize時,空閑線程的存活時間
unit
keepAliveTime的時間單位
workQueue
任務隊列,被提交但未被執行的任務存放的地方
threadFactory
線程工廠,用於創建線程,可使用默認的線程工廠或者自定義線程工廠
handler
任務拒絕策略,由於任務過多或其他原因導致線程池無法處理時使用的任務拒絕策略

 

 

 

 

 

 

 

 

 

 

 

1.3 線程池的工作流程

  Java線程池的工作流程為:

  1. 線程池剛被創建時,只是向系統申請一個用於執行線程隊列和管理線程池的線程資源。在調用execute()添加任務時,線程池開始按流程執行任務。
  2. 如果正在運行的線程數量少於corePoolSize,線程池就會立刻創建線程並執行該線程任務。
  3. 如果正在運行的線程數量大於等於corePoolSize,該任務將被放入阻塞隊列中。
  4. 在阻塞隊列已滿且正在運行的線程數量小於maximumPoolSize時,線程池會創建非核心線程立刻執行該線程任務。
  5. 在阻塞隊列已滿且正在運行的線程數量大於等於maximumPoolSize時,線程池將拒絕執行該線程任務並拋出RejectExecutionException異常。
  6. 在線程任務執行完畢后,該任務將被從線程池隊列中移除,線程池將從隊列中取下一個線程任務繼續執行。
  7. 在線程處於空閑狀態的時間超過keepAliveTime時間時,正在運行的線程數量超過corePoolSize,該線程將會被認定為空閑線程並停止。在線程池中所有線程任務都執行完畢后,線程池會收縮到corePoolSize大小。

具體流程如圖:

1.4 線程池的拒絕策略

  JDK內置的拒絕策略有AboutPolicy,CallerRunsPolicy,DiscardOldestPolicy,DiscardPolicy這4種,也可以自定義拒絕策略。

  1、AboutPolicy

  AboutPolicy直接拋出異常,阻止線程正常運行。

具體的JDK源碼如下:

public static class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() { }

        //直接拋出異常信息,不做任何處理
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
    }
}

  2、CallerRunsPolicy

  CallerRunsPolicy的拒絕策略為:如果被丟棄的線程任務未關閉,則執行該線程任務。其實CallerRunsPolicy拒絕策略不會真的丟棄任務。

具體的JDK源碼如下:

    public static class CallerRunsPolicy implements RejectedExecutionHandler {
       
        public CallerRunsPolicy() { }

        //執行被丟棄的任務
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

  3、DiscardOldestPolicy

  DiscardOldestPolicy的拒絕策略為:移除線程隊列中最早的一個線程任務,並嘗試提交當前任務。

具體的JDK源碼如下:

    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        
        public DiscardOldestPolicy() { }

        
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();//丟棄線程隊列中最老的一個線程任務
                e.execute(r);//嘗試提交當前任務
            }
        }
    }

  4、DiscardPolicy

  DiscardPolicy的拒絕策略為:丟棄當前的線程任務而不做任何處理。如果系統允許在資源不足的情況下丟棄部分任務,可以保證系統安全和穩定。

具體的JDK源碼如下:

    public static class DiscardPolicy implements RejectedExecutionHandler {
       
        public DiscardPolicy() { }

        //直接丟棄線程,不做任何處理
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }

 

2、5種常用線程池

  ExecutorService接口有多個實現類可用於創建不同的線程池。如下是5種常用的線程池。

名稱 說明
newCachedThreadPool 可緩存的線程池
newFixedThreadPool 固定大小的線程池
newScheduledThreadPool 可做任務調度的線程池
newSingleThreadExecutor 單個線程的線程池
newWorkStealingPool 足夠大的線程池,JDK1.8新增

 

 

 

 

 

 

 

 

2.1 newCachedThreadPool

  newCachedThreadPool用於創建一個緩存線程池。其在創建新線程時,如果有可重用線程,則重用這些線程,否則重新創建一個新的線程並將其添加到線程池中。

  newCachedThreadPool適用於執行時間很短的大量任務的情況。

創建方式如下:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

2.2 newFixedThreadPool

  newFixedThreadPool用於創建一個固定線程數量的線程池,並將線程資源存放在隊列中循環使用。

  在newFixedThreadPool線程池中,如果活動狀態的線程數量大於等於核心線程池線程數量,則新提交的任務將在阻塞隊列中排隊,直到有可用的線程資源。

創建方式如下:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);

2.3 newScheduledThreadPool

  newScheduledThreadPool創建一個可定時調度的線程池,可設置在給定的延遲時間后執行或者定期執行某個線程任務。

創建方式如下:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
//創建一個延遲3秒執行的線程
scheduledThreadPool.schedule(myRunnable,3, TimeUnit.SECONDS);
//創建一個延遲1秒執行且每3秒執行一次的線程
scheduledThreadPool.scheduleAtFixedRate(myRunnable,1,3,TimeUnit.SECONDS);

2.4 newSingleThreadExecutor

  newSingleThreadExecutor線程池永遠保證有且僅有一個可用線程,即便該線程停止或發生異常,newSingleThreadExecutor會啟動一個新的線程來代替該線程。

創建方式如下:

ExecutorService singleThread = Executors.newSingleThreadExecutor();

2.5 newWorkStealingPool

  newWorkStealingPool創建足夠的線程的線程池以達到快速運算的目的。JDK根據當前線程的運行需求向操作系統申請足夠的線程。

創建方式如下:

ExecutorService workStealingPool = Executors.newWorkStealingPool();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2024 CODEPRJ.COM