【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秒未被使用的線程。java

若是線程有可用的,就使用以前建立好的線程,緩存

若是線程沒有可用的,就新建立線程。服務器

  • 重用:緩存型池子,先查看池中有沒有之前創建的線程,若是有,就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中,有一個固定大小的池。ide

若是當前須要執行的任務超過池大小,那麼多出的任務處於等待狀態,直到有空閒下來的線程執行任務,源碼分析

若是當前須要執行的任務小於池大小,空閒的線程也不會去銷燬。測試

    • 重用: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>());  
}  

 執行結果:spa

第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的區別?

相關文章
相關標籤/搜索