1. 程式人生 > 實用技巧 >java中常見的四種執行緒池

java中常見的四種執行緒池

前言:

我們之前使用執行緒的時候都是使用new Thread來進行執行緒的建立,但是這樣會有一些問題。如:

a. 每次new Thread新建物件效能差。
b. 執行緒缺乏統一管理,可能無限制新建執行緒,相互之間競爭,及可能佔用過多系統資源導致宕機或oom。
c. 缺乏更多功能,如定時執行、定期執行、執行緒中斷。
相比new Thread,Java提供的四種執行緒池的好處在於:
a. 重用存在的執行緒,減少物件建立、消亡的開銷,效能佳。
b. 可有效控制最大併發執行緒數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
c. 提供定時執行、定期執行、單執行緒、併發數控制等功能。

而我們今天來學習和掌握另外一個新的技能,特別像一個執行緒池的一個介面類ExecutorService,下面我們來了解下java中Executors的執行緒池

Java通過Executors提供四種執行緒池,分別為:

  newCachedThreadPool:建立一個可快取的執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。

  newFixedThreadPool:建立一個定長的執行緒池,可控制執行緒的最大併發數,超出的執行緒在佇列中等待。

  看看Executors工廠內部是如何實現的

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

  

  可以看到返回的是一個ThreadPoolExecutor物件,核心執行緒數和是最大執行緒數都是傳入的引數,存活時間是0,時間單位是毫秒,阻塞佇列是無界佇列LinkedBlockingQueue。

  由於佇列採用的是無界佇列LinkedBlockingQueue,最大執行緒數maximumPoolSize和keepAliveTime都是無效引數,拒絕策略也將無效,為什麼?

  這裡又延伸出一個問題,無界佇列說明任務沒有上限,如果執行的任務比較耗時,那麼新的任務會一直存放線上程池中,執行緒池的任務會越來越多

  示例程式碼:

    

public class Main {

    
public static void main(String[] args){ ExecutorService pool = Executors.newFixedThreadPool(4); for (int i = 0; i < 8; i++) { int finalI = i + 1; pool.submit(() -> { try { System.out.println("任務"+ finalI +":開始等待2秒,時間:"+LocalTime.now()+",當前執行緒名:"+Thread.currentThread().getName()); Thread.sleep(2000); System.out.println("任務"+ finalI +":結束等待2秒,時間:"+LocalTime.now()+",當前執行緒名:"+Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } }); } pool.shutdown(); } }

  newCachedThreadPool方法也是返回ThreadPoolExecutor物件,核心執行緒是0,最大執行緒數是Integer的最MAX_VALUE,存活時間是60,時間單位是秒,SynchronousQueue佇列。

  從傳入的引數可以得知,在newCachedThreadPool方法中的空閒執行緒存活時間時60秒,一旦超過60秒執行緒就會被終止。這邊還隱含了一個問題,如果執行的執行緒較慢,而提交任務的速度快於執行緒執行的速度,那麼就會不斷的建立新的執行緒,從而導致cpu和記憶體的增長。

  程式碼和newFixedThreadPool一樣迴圈新增新的執行緒任務,我的電腦執行就會出現如下錯誤

  newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行。

  

看看Executors工廠內部是如何實現的

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

這裡返回的是ScheduledThreadPoolExecutor物件,我們繼續深入進去看看

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

這裡呼叫的是父類的建構函式,ScheduledThreadPoolExecutor的父類是ThreadPoolExecutor,所以返回的也是ThreadPoolExecutor物件。核心執行緒數是傳入的引數corePoolSize,執行緒最大值是Integer的MAX_VALUE,存活時間時0,時間單位是納秒,佇列是DelayedWorkQueue。

public class ScheduledThreadPoolExecutor
        extends ThreadPoolExecutor
        implements ScheduledExecutorService {}

下面是ScheduledExecutorService的一些方法

public interface ScheduledExecutorService extends ExecutorService {
	//delay延遲時間,unit延遲單位,只執行1次,在經過delay延遲時間之後開始執行
    public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
    public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);
	//首次執行時間時然後在initialDelay之後,然後在initialDelay+period 後執行,接著在 initialDelay + 2 * period 後執行,依此類推
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,
                                                  long period,
                                                  TimeUnit unit);
	//首次執行時間時然後在initialDelay之後,然後延遲delay時間執行
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);
}

  newSingleThreadExecutor:建立一個單執行緒化的執行緒池,它只會有唯一的工作執行緒來執行任務,保證所有任務按照指定順序執行。

參考連結:https://www.cnblogs.com/fixzd/p/9125737.html