概念
构建了一个生产者消费者模型,将线程和任务解耦,并不直接关联,复用线程。
线程池的状态
主要参数
- 核心线程数
- 最大线程数
- 存活时间
- 存活时间的单位
- 阻塞队列
- 拒绝策略
工作流程
常见的线程池
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
阻塞队列为无界队列,队列容量为 Integer.MAX_VALUE。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
阻塞队列为无界队列,大量请求可能导致堆积请求,而耗费非常大的内存。
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
newScheduledThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
- newFixedThreadPool和newSingleThreadExecutor: 主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
- newCachedThreadPool和newScheduledThreadPool: 主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
阻塞队列的使用场景
参考
ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。(默认长度为Integer.MAX_VALUE)
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。ArrayBlockingQueue
基于数组、读写共用一把锁(RenentrantLock),无法真正意义上进行读写并行。
适用于项目中,并发量不高的地方。
固定容量,如果容量太小,取数据比存数据慢,会阻塞大量线程
LinkedBlockingQueue (推荐)
基于链表,默认整数最大值容量。
读写锁分离,适合高并发场景。
如果取数据过慢,可能导致大量任务堆积,导致内存被消耗。
PriorityBlockingQueue
支持任务排序,用在分级消费的场景:例如VIP用户优先处理。
DelayQueue
延迟队列,基于优先级队列实现。比较的是时间。
场景: 订单超时取消功能
用户下订单未支付开始倒计时,超时则释放订单中的资源,如果取消或者完成支付,我们再讲队列中的数据移除掉。
SynchronousQueue
采用双栈双队列算法的无空间队列或栈 任何一个对SynchronousQueue写需要等到一个对SynchronousQueue的读操作,任何一个个读操作需要等待一个写操作 没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者。
分析: 相当于是交换通道,不存储任何元素,提供者和消费者是需要组队完成工作,缺少一个将会阻塞线程,直到等到配对为止
场景:newCachedThreadPool
,轻量级别任务转交。
这个线程池根据需要(新任务到来时)创建新的线程,如果有空闲线程则会重复使用,线程默认空闲了60秒后会被回收
拒绝策略
线程池参数配置
尽量使用有界阻塞队列。
- CPU密集型:尽可能少的线程,Ncpu+1
- IO密集型:尽可能多的线程,Ncpu * 2。(例如数据库连接池)
参考资料
链接