Executor
架構最核心的類是ThreadPoolExecutor,它是線程池的實作類,詳情請看ThreadPoolExecutor 源碼解析。
Executors
還為我們提供了4種常見的線程池,他們都是基于ThreadPoolExecutor而來,還有一個是
WorkStealingPool
是基于ForkJoinPool的。
FixedThreadPool
使用方式
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
建立一個工作線程數為10的線程池。
實作
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
我們可以看到:
- 它其實就是建立了一個核心線程數(corePoolSize)和最大線程數(maximumPoolSize)都是
的nThreads
ThreadPoolExecutor
-
為0表示線程一旦空閑就會被回收keepAliveTime
- 它使用了
隊列來存儲任務,但是該隊列的長度預設是LinkedBlockingQueue
長度,當任務過多時有可能會發生記憶體溢出,是以呢不建議使用。Integer.MAX_VALUE
運作示意圖
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CMxITM5ImN4YWO3EWZ3QjNzYzX3IzM1ATM0AzLcFTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
SingleThreadExecutor
使用方式
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
建立一個工作線程數為1的線程池。
實作
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
我們可以看到:
- 它其實就是建立了一個核心線程數(corePoolSize)和最大線程數(maximumPoolSize)都是
的1
ThreadPoolExecutor
-
為0表示線程一旦空閑就會被回收keepAliveTime
- 它也使用了
隊列來存儲任務,但是該隊列的長度預設是LinkedBlockingQueue
長度,當任務過多時有可能會發生記憶體溢出,是以呢不建議使用。Integer.MAX_VALUE
SingleThreadExecutor會将送出的任務按照送出順序串行執行
運作示意圖
CachedThreadPool
使用方式
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
建立一個最大工作線程數是
Integer.MAX_VALUE
的線程池。
實作
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
我們可以看到:
- 建立了一個核心線程數(corePoolSize)是0,最大線程數(maximumPoolSize)是
的Integer.MAX_VALUE
,該線程池的線程數不可控,極端情況下, 可能會因為建立過多線程而耗盡CPU和記憶體資源,不建議使用ThreadPoolExecutor
- 空閑線程的存活時間是1分鐘
- 它也使用了
隊列來存儲任務,該隊列預設不存儲元素,每一個put操作必須等待一個take操作,否則不能添加元素。當一直有任務送出的時候,改線程池會不斷的新增工作線程來執行任務SynchronousQueue
運作示意圖
前面提到過,SynchronousQueue是一個沒有容量的阻塞隊列。每個插入操作必須等待另一個線程的對應移除操作,反之亦然。CachedThreadPool使用SynchronousQueue,把主線程送出的任務傳遞給空閑線程執行。CachedThreadPool中任務傳遞的示意圖如圖:
ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor繼承自ThreadPoolExecutor。它主要用來在給定的延遲之後運作任務,或者定期執行任務。ScheduledThreadPoolExecutor的功能與Timer類似,但 ScheduledThreadPoolExecutor功能更強大、更靈活。
使用方式
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);
scheduledThreadPool.scheduleAtFixedRate(() -> {
System.out.println(Thread.currentThread().getName() + " " + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
//0表示首次執行任務的延遲時間,1表示每次執行任務的間隔時間,TimeUnit.SECONDS執行的時間間隔數值機關
}, 0, 1, TimeUnit.SECONDS);
建立一個核心線程時10,最大工作線程數是
Integer.MAX_VALUE
的線程池。
運作結果:
Connected to the target VM, address: '127.0.0.1:50560', transport: 'socket'
pool-4-thread-1 2019-08-06T15:57:08.942
pool-4-thread-1 2019-08-06T15:57:09.802
pool-4-thread-2 2019-08-06T15:57:10.803
pool-4-thread-1 2019-08-06T15:57:11.803
pool-4-thread-3 2019-08-06T15:57:12.803
pool-4-thread-2 2019-08-06T15:57:13.803
實作
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
我們可以看到:
- 建立了一個核心線程數(corePoolSize)是10,最大線程數(maximumPoolSize)是
的Integer.MAX_VALUE
,該線程池的線程數不可控,極端情況下, 可能會因為建立過多線程而耗盡CPU和記憶體資源,不建議使用ThreadPoolExecutor
- 空閑線程的存活時間是0,一但空閑就會被回收掉
- 它也使用了
隊列來存儲任務,它是一個隻能存儲DelayedWorkQueue
任務的延遲隊列,一般我們使用他的子類RunnableScheduledFuture
。ScheduledFutureTask
ScheduledFutureTask類圖
ScheduledFutureTask核心屬性
/** 表示這個任務被添加到ScheduledThreadPoolExecutor中的序号 */
private final long sequenceNumber;
/** 表示這個任務将要被執行的具體時間 */
private long time;
/** 表示任務執行的間隔周期 */
private final long period;
運作示意圖
WorkStealingPool
使用方式
ExecutorService workStealingPool = Executors.newWorkStealingPool();
建立一個ForkJoinPool線程池,線程池預設大小是CPU核心數。
實作
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
我們可以看到:
- 它其實就是建立了一個核心線程數是CPU核心數的
ForkJoinPool
總結
從上面我們可以看到,大部分的線程池是不建議直接使用的,我們在使用線程池的時候盡量使用
ThreadPoolExecutor
來建立,并且根據業務合理的設定
corePoolSize
、
maximumPoolSize
、
keepAliveTime
、
BlockingQueue
、
RejectedExecutionHandler
的值,否則線上上環境很容易耗盡CPU和記憶體資源,導緻服務不可用。
參考
《java并發程式設計的藝術》
源碼
https://github.com/wyh-spring-ecosystem-student/spring-boot-student/tree/releases