1、背景java
線程屬於系統稀缺資源,在使用線程時,若是無限制建立線程,達到CPU高負荷時,會致使系統運行緩慢,更有甚者直接宕機。數組
在這樣的基礎上咱們但願在使用線程時,竟可能使系統線程數處於一個可控範圍,儘量實現線程的重用。併發
2、Executors 分析app
Executors 示例 DEMOide
/** * @author binH * @date 2018/01/24 */ package org.lsnbin.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadAnalyze { private static ExecutorService fexecutor = Executors.newFixedThreadPool(20); public static void main(String[] args) { for (int i = 0; i < 20; i++) { fexecutor.execute(new ThreadAnalyze().new Task()); } } private class Task implements Runnable{ @Override public void run() { System.out.println("Thread name --> " + Thread.currentThread().getName()); } } }
示例分析:高併發
一、使用Executors初始化一個包含10個線程的線程池性能
二、使用execute方法提交20個任務,打印線程名this
三、負責執行任務的線程的生命週期交由Executors管理。spa
3、Executors 內部分析線程
Executors是線程池的工廠類,內部基於ThreadPoolExecutor實現,對ThreadPoolExecutor進行封裝,並提供相對應方法。
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
初始化必定數量的線程池,當corePoolSize=maximumPoolSize時,使用LinkedBlockingQueue做爲阻塞隊列,不過當線程池不使用時,也不會釋放線程。
newCachedThreadPool
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
不初始化線程,在須要使用時建立線程,最大容許數爲Integer.MAX_VALUE,使用SynchronousQueue做爲阻塞隊列,當線程空閒時keepAliveTime後釋放線程。
容易存在問題:在高併發狀況下,一瞬間會建立大量的線程,會出現嚴重的性能問題。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
初始化只有一個線程的線程池,若是該線程因爲異常結束,會從新建立一個新的線程。線程不過時,使用LinkedBlockingQueue做爲阻塞隊列。惟一個線程能夠保證任務的順序執行。
newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }
初始化必定數量線程的線程池,線程池中的任務能夠在指定的時間內週期性的執行所提交的任務。可用於按期同步。
newWorkStealingPool -- JDK1.8
public static ExecutorService newWorkStealingPool(int parallelism) { return new ForkJoinPool (parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); }
建立持有足夠線程的線程池來支持給定的並行級別,並經過使用多個隊列來減小競爭,須要指定parallelism並行參數,若是沒指定,則默認CPU數量。
ForkJoinPool:支持大任務分解成小任務的線程池,這是Java8新增線程池,一般配合ForkJoinTask接口的子類RecursiveAction或RecursiveTask使用。
4、ThreadPoolExecutor 分析
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
參數分析:
一、corePoolSize
線程池核心線程數。當線程池提交一個任務時,線程池就會建立一個新線程並執行任務,直到線程池內運行狀態的線程數等於corePoolSize。
當繼續有任務提交時,會被放入阻塞隊列中。使用 prestartCoreThread() 方法能夠提早預建立並啓動全部核心線程。
二、maximumPoolSize
線程池最大可用線程數。在線程數等於corePoolSize時,若是當前阻塞隊列滿了,繼續提交任務時會建立新的線程並執行任務。前提時線程數小於maximumPoolSize。
三、keepAliveTime
線程池中線程空閒存活時間。默認狀況下該狀況只會在大於corePoolSize時有用。可是隻要keepAliveTime值非0,能夠經過allowallowCoreThreadTimeOut(boolean value) 方法將超時策略應用於核心線程。
四、unit
keepAliveTime存活時間的單位。
五、workQueue
用來保存等待被執行的任務的阻塞隊列。JDK實現的隊列有如下幾種:
ArrayBlockingQueue:基於數組結構的有界阻塞隊列,按照FIFO ( 先進先出 ) 排序任務。
LinkedBlockingQueue:基於鏈表結構的阻塞隊列,按照FIFO ( 先進先出 ) 排序任務。效果比ArrayBlockingQueue好。
DelayQueue:具備延時執行的無界阻塞隊列。
SynchronousQueue:一個不能存儲任何元素的阻塞隊列,每一個插入的操做必須等待另外一個相應的刪除操做,不然插入則會一直處於阻塞狀態。
第二個線程必須等待前一個線程結束。
PriorityBlockingQueue:具備 優先級的無界阻塞隊列。
六、threadFactory
建立線程的工廠
默認工廠實現:
DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; }
七、handler
線程池的飽和策略,用於線程數飽和與阻塞隊列已滿的狀況。有任務繼續提交時觸發的處理邏輯。線程池提供了4種策略:
AbortPolicy:直接跑出異常,默認策略。
DiscardPolicy:直接丟棄任務,不處理。
DiscardOldestPolicy:丟棄阻塞隊列中靠前的舊的任務,執行新的任務。
CallerRunsPolicy:用調用者的線程來執行任務。