[Q&A] 如何创建线程池?
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
corePoolSize
# 核心线程数,线程池的大小,the number of threads to keep in the pool, even if they are idle.
maximumPoolSize
# 线程池最大数量 the maximum number of threads to allow in the pool.
keepAliveTime
# when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
unit
# 可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微秒)。
BlockingQueue<Runnable> workQueue
# the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.
threadFactory
# 创建线程的工厂,the factory to use when the executor creates a new thread
RejectedExecutionHandler handler
# 拒绝策略,the handler to use when execution is blocked because the thread bounds and queue capacities are reached
# 当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。
线程池 饱和策略
线程池 任务队列
缓存型线程池 newCachedThreadPool
# newCachedThreadPool
ExecutorService exec = Executors.newCachedThreadPool();
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
# 无界的线程池
# 适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器。
[Q&A] 为什么不推荐使用CachedThreadPool
?
maximumPoolSize被设置为 Integer.MAX.VALUE
, 这也就意味着如果主线程提交任务的速度
高于maximumPool中线程处理任务的速度
时,CachedThreadPool 会不断创建新的线程。极端情况下,这样会导致耗尽cpu和内存资源,从而导致OOM
。
固定线程数量线程池 newFixedThreadPool
# newFixedThreadPool
ExecutorService exec = Executors.newFixedThreadPool(3);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
# 无界的线程池
# 适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景,它适用于负载比较重的服务器。
[Q&A] 为什么不推荐使用FixedThreadPool
?
1、 当线程池中的线程数达到 corePoolSize
后,新任务将会添加到无界队列LinkedBlockingQueue
中等待, 故 maximumPoolSize
将是一个无效参数。所以corePoolSize 和 maximumPoolSize 被设置为同一个值。 keepAliveTime
也是一个无效参数,因为 keepAliveTime 针对超过 corePoolSize 的线程起作用。
2、 运行中
的 FixedThreadPool 不会拒绝任务(前提是未执行 shutdown()
或 shutdownNow()
)在任务比较多的时候会导致 OOM
(内存溢出)。
单线程线程池 newSingleThreadExecutor
# newSingleThreadExecutor
ExecutorService exec = Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1,1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}
# 无界的线程池
# 用于需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景。
[Q&A] 为什么不推荐使用SingleThreadExecutor
?
SingleThreadExecutor
就是特殊的 FixedThreadPool
,理由如上
周期性线程池 newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));
}
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService
{
public ScheduledThreadPoolExecutor(int corePoolSize) {
ThreadPoolExecutor (corePoolSize, Integer.MAX_VALUE, 10, TimeUnit.MILLI_SCALE, new DelayedWorkQueue());
}
}
# newScheduledThreadPool 固定个数线程的调度线程池
# 用于需要多个后台线程执行周期任务,同时为了满足资源管理的需求而需要限制后台线程的数量的应用场景。
ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
ScheduledThreadPoolExecutor exec = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(5);
ScheduledExecutorService exec = Executors.newScheduledThreadPool(2);
exec.schedule(() -> {执行}, 10, TimeUnit.SECONDS); // 延迟10秒执行
exec.scheduleWithFixedDelay(() -> {执行}, 1, 4, TimeUnit.SECONDS); // 延迟 1s,间隔 4s
exec.scheduleAtFixedRate(() -> {执行}, 1, 4, TimeUnit.SECONDS) // 延迟 1s,周期 4s
-----------------------------------------------------------------------------------------------
# newSingleThreadScheduledExecutor 单个线程的调度线程池
ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
ScheduledExecutorService exec = Executors.newScheduledThreadPool(1);
# 用于需要单个后台线程执行周期任务,同时需要保证顺序地执行各个任务的应用场景。
定时器Timer
[Q&A] Timer
和ScheduleThreadPoolExecutor
区别
Timer | ScheduleThreadPoolExecutor |
---|---|
指定绝对时间 | 可以用相对时间 |
对系统时钟的变化敏感 | 不是 |
单线程 | 使用线程池。可以配置任意数量的线程 |
抛出运行时异常会杀死一个线程从而导致Timer 死机 | 可以捕获运行时异常且处理它们。 抛出异常的任务将被取消其他任务将继续运行 |
自定义线程池 ThreadPoolExecutor
# 自定义线程池
ThreadPoolExecutor exec = new ThreadPoolExecutor(3, 5, 50, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(20));
[Q&A]自定义线程好处?
1、 避免了上面的 OOM
问题 ( 自定义线程池 使用 有界队列 ,控制 maximumPoolSize 数量 )
2、 实际使用中可以根据自己机器的性能、业务场景来手动配置
线程池的参数。比如核心线程数、使用的任务队列、饱和策略等等
3、 我们可以显示地给我们的线程池命名
,这样有助于我们定位问题
[Q&A] 线程池好的实践方案?
1、自定义线程池,使用 ThreadPoolExecutor
的构造函数声明线程池
2、给线程池命名和业务挂靠
3、监测线程池运行状态
4、建议不同类别的业务用不同的线程池(尤其杜绝父任务和子任务用同一个线程池)
-----------------------------------------------------------------------------读书笔记摘自 书名:Java并发编程的艺术 作者:方腾飞;魏鹏;程晓明