【Java線程】Java線程池ExecutorService


示例

import java.util.concurrent.ExecutorService;  
import java.util.concurrent.Executors;  
import java.util.concurrent.ScheduledExecutorService;  
  
public class Ch09_Executor {  
      
   private static void run(ExecutorService threadPool) {  
    for(int i = 1; i < 5; i++) {    
            final int taskID = i;    
            threadPool.execute(new Runnable() {    
                @Override  
        public void run() {    
                    for(int i = 1; i < 5; i++) {    
                        try {    
                            Thread.sleep(20);// 為了測試出效果,讓每次任務執行都需要一定時間    
                        } catch (InterruptedException e) {    
                            e.printStackTrace();    
                        }    
                        System.out.println("" + taskID + "次任務的第" + i + "次執行");    
                    }    
                }    
            });    
        }    
        threadPool.shutdown();// 任務執行完畢,關閉線程池    
   }  
      
    public static void main(String[] args) {  
        // 創建可以容納3個線程的線程池  
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  
          
        // 線程池的大小會根據執行的任務數動態分配  
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  
          
            // 創建單個線程的線程池,如果當前線程在執行任務時突然中斷,則會創建一個新的線程替代它繼續執行任務    
        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();  
          
        // 效果類似於Timer定時器  
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);  
          
        run(fixedThreadPool);  
//      run(cachedThreadPool);  
//      run(singleThreadPool);  
//      run(scheduledThreadPool);  
    }  
  
}  

CachedThreadPool

CachedThreadPool會創建一個緩存區,將初始化的線程緩存起來。會終止並且從緩存中移除已有60秒未被使用的線程。

如果線程有可用的,就使用之前創建好的線程,

如果線程沒有可用的,就新創建線程。

  • 重用:緩存型池子,先查看池中有沒有以前建立的線程,如果有,就reuse;如果沒有,就建一個新的線程加入池中
  • 使用場景:緩存型池子通常用於執行一些生存期很短的異步型任務,因此在一些面向連接的daemon型SERVER中用得不多。
  • 超時:能reuse的線程,必須是timeout IDLE內的池中線程,缺省timeout是60s,超過這個IDLE時長,線程實例將被終止及移出池。
  • 結束:注意,放入CachedThreadPool的線程不必擔心其結束,超過TIMEOUT不活動,其會自動被終止。
// 線程池的大小會根據執行的任務數動態分配  
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  
  
public static ExecutorService newCachedThreadPool() {  
    return new ThreadPoolExecutor(0,                 //core pool size  
                                  Integer.MAX_VALUE, //maximum pool size  
                                  60L,               //keep alive time  
                                  TimeUnit.SECONDS,  
                                  new SynchronousQueue<Runnable>());  
}  

執行結果:

第1次任務的第1次執行  
第4次任務的第1次執行  
第3次任務的第1次執行  
第2次任務的第1次執行  
第3次任務的第2次執行  
第4次任務的第2次執行  
第2次任務的第2次執行  
第1次任務的第2次執行  
第2次任務的第3次執行  
第4次任務的第3次執行  
第3次任務的第3次執行  
第1次任務的第3次執行  
第2次任務的第4次執行  
第1次任務的第4次執行  
第3次任務的第4次執行  
第4次任務的第4次執行  

4個任務是交替執行的

FixedThreadPool

在FixedThreadPool中,有一個固定大小的池。

如果當前需要執行的任務超過池大小,那么多出的任務處於等待狀態,直到有空閑下來的線程執行任務,

如果當前需要執行的任務小於池大小,空閑的線程也不會去銷毀。

    • 重用:fixedThreadPool與cacheThreadPool差不多,也是能reuse就用,但不能隨時建新的線程
    • 固定數目:其獨特之處在於,任意時間點,最多只能有固定數目的活動線程存在,此時如果有新的線程要建立,只能放在另外的隊列中等待,直到當前的線程中某個線程終止直接被移出池子
    • 超時:和cacheThreadPool不同,FixedThreadPool沒有IDLE機制(可能也有,但既然文檔沒提,肯定非常長,類似依賴上層的TCP或UDP IDLE機制之類的),
    • 使用場景:所以FixedThreadPool多數針對一些很穩定很固定的正規並發線程,多用於服務器
    • 源碼分析:從方法的源代碼看,cache池和fixed 池調用的是同一個底層池,只不過參數不同:
      fixed池線程數固定,並且是0秒IDLE(無IDLE)
      cache池線程數支持0-Integer.MAX_VALUE(顯然完全沒考慮主機的資源承受能力),60秒IDLE
// 創建可以容納3個線程的線程池  
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  
  
public static ExecutorService newFixedThreadPool(int nThreads) {  
        return new ThreadPoolExecutor(nThreads, //core pool size  
                                      nThreads, //maximum pool size  
                                      0L,       //keep alive time  
                                      TimeUnit.MILLISECONDS,  
                                      new LinkedBlockingQueue<Runnable>());  
}  

 執行結果:

第1次任務的第1次執行  
第3次任務的第1次執行  
第2次任務的第1次執行  
第3次任務的第2次執行  
第2次任務的第2次執行  
第1次任務的第2次執行  
第3次任務的第3次執行  
第1次任務的第3次執行  
第2次任務的第3次執行  
第3次任務的第4次執行  
第1次任務的第4次執行  
第2次任務的第4次執行  
第4次任務的第1次執行  
第4次任務的第2次執行  
第4次任務的第3次執行  
第4次任務的第4次執行  

創建了一個固定大小的線程池,容量為3,然后循環執行了4個任務。由輸出結果可以看到,前3個任務首先執行完,然后空閑下來的線程去執行第4個任務

SingleThreadExecutor 

SingleThreadExecutor得到的是一個單個的線程,這個線程會保證你的任務執行完成。

如果當前線程意外終止,會創建一個新線程繼續執行任務,這和我們直接創建線程不同,也和newFixedThreadPool(1)不同。

// 創建單個線程的線程池,如果當前線程在執行任務時突然中斷,則會創建一個新的線程替代它繼續執行任務    
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();  
  
public static ExecutorService newSingleThreadExecutor() {  
        return new FinalizableDelegatedExecutorService  
            (new ThreadPoolExecutor(1,  //core pool size  
                                    1,  //maximum pool size  
                                    0L, //keep alive time  
                                    TimeUnit.MILLISECONDS,  
                                    new LinkedBlockingQueue<Runnable>()));  
}  

執行結果:

第1次任務的第1次執行  
第1次任務的第2次執行  
第1次任務的第3次執行  
第1次任務的第4次執行  
第2次任務的第1次執行  
第2次任務的第2次執行  
第2次任務的第3次執行  
第2次任務的第4次執行  
第3次任務的第1次執行  
第3次任務的第2次執行  
第3次任務的第3次執行  
第3次任務的第4次執行  
第4次任務的第1次執行  
第4次任務的第2次執行  
第4次任務的第3次執行  
第4次任務的第4次執行  

4個任務是順序執行的

ScheduledThreadPool

ScheduledThreadPool是一個固定大小的線程池,與FixedThreadPool類似,執行的任務是定時執行

// 效果類似於Timer定時器  
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);  
  
public ScheduledThreadPoolExecutor(int corePoolSize) {  
        super(corePoolSize,      //core pool size  
              Integer.MAX_VALUE, //maximum pool size  
              0,                 //keep alive time  
              TimeUnit.NANOSECONDS,  
              new DelayedWorkQueue());  
}  

執行結果:

第1次任務的第1次執行  
第2次任務的第1次執行  
第3次任務的第1次執行  
第2次任務的第2次執行  
第1次任務的第2次執行  
第3次任務的第2次執行  
第2次任務的第3次執行  
第1次任務的第3次執行  
第3次任務的第3次執行  
第2次任務的第4次執行  
第1次任務的第4次執行  
第3次任務的第4次執行  
第4次任務的第1次執行  
第4次任務的第2次執行  
第4次任務的第3次執行  
第4次任務的第4次執行  

——與FixedThreadPool的區別?

 


免責聲明!

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



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