线程池原理分析
首先要明确为什么要使用线程池,使用线程池会带来什么好处?
- 线程是稀缺资源,不能频繁的创建。
- 应当将其放入一个池子中,可以给其他任务进行复用。
- 解耦作用,线程的创建于执行完全分开,方便维护。
创建一个线程池
以一个使用较多的
1 | ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) |
为例:
- 其中的
corePoolSize
为线程池的基本大小。 maximumPoolSize
为线程池最大线程大小。keepAliveTime
和unit
则是线程空闲后的存活时间。workQueue
用于存放任务的阻塞队列。handler
当队列和最大线程池都满了之后的饱和策略。
处理流程
当提交一个任务到线程池时它的执行流程是怎样的呢?
首先第一步会判断核心线程数有没有达到上限,如果没有则创建线程(会获取全局锁),满了则会将任务丢进阻塞队列。
如果队列也满了则需要判断最大线程数是否达到上限,如果没有则创建线程(获取全局锁),如果最大线程数也满了则会根据饱和策略处理。
常用的饱和策略有:
- 直接丢弃任务。
- 调用者线程处理。
- 丢弃队列中的最近任务,执行当前任务。
所以当线程池完成预热之后都是将任务放入队列,接着由工作线程一个个从队列里取出执行。
合理配置线程池
线程池并不是配置越大越好,而是要根据任务的熟悉来进行划分:
如果是 CPU
密集型任务应当分配较少的线程,比如 CPU
个数相当的大小。
如果是 IO 密集型任务,由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如 CPU 个数 * 2
。
当是一个混合型任务,可以将其拆分为 CPU
密集型任务以及 IO
密集型任务,这样来分别配置。