Executors目前提供了5种不同的线程池创建配置:
-
newCachedThreadPool(),它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过60秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用SynchronousQueue作为工作队列。
-
newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,任何时候最多nThreads个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目nThreads。其内部使用LinkedBlockingQueue作为工作队列。
-
newSingleThreadExecutor(),它的特点在于工作线程数目被限制为1,操作LinkedBlockingQueue工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目。
-
newSingleThreadScheduledExecutor()和newScheduledThreadPool(int corePoolSize),创建的是个ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。
-
newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序。
那么,线程池是怎样配置的呢?我们先来看看它的源代码:
//除了newWorkStealingPool(),都调用这个构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
里面的参数分别是
- corePoolSize, 稍微的核心线程数,可以大致理解为长期驻留的线程数目。
- maximumPoolSize,顾名思义,就是线程不够时能够创建的最大线程数。
- keepAliveTime,指定了额外的线程能够闲置多久。
- TimeUnit,时间的单位。
- workQueue,工作队列,必须是BlockingQueue(有界的)。
阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。
在现实应用中,理解应用于线程池的交互和线程池内部的工作过程,可以参考下图。
最后总结一下线程池的规则
- 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,及时核心线程有空闲的
- 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列WorkQueue,而不添加新的线程。
- 如果无法将请求加入队列 (如SynchronousQueue)或者队列已满,则创建非核心线程,除非创建此线程超出 maximumPoolSize,如果超过,在这种情况下,新的任务么就会根据拒绝策略来拒绝执行
参考链接:https://www.cnblogs.com/gujiande/p/9488462.html
https://blog.csdn.net/u011517841/article/details/79810689