线程池的处理流程
...大约 3 分钟
线程池的处理流程
1. 处理流程
创建线程池需要使用ThreadPoolExecutor 类,他的构造函数参数如下
public ThreadPoolExecutor(int corePoolSize, //核心线程的数量
int maximumPoolSize, //最大线程数量
long keepAliveTime, //超出核心线程数量以外的线程空余存活时间
TimeUnit unit, //存活时间的单位
BlockingQueue<Runnable> workQueue, //保存待执行任务的队列
ThreadFactory threadFactory, //创建新线程使用的工厂
RejectedExecutionHandler handler // 当任务无法执行时的处理器
) {...}
参数介绍如注释所示,要了解这些参数左右着什么,就需要了解线程池具体的执行方法ThreadPoolExecutor.execute
:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//1.当前池中线程比核心数少,新建一个线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2.核心池已满,但任务队列未满,添加到队列中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command)) //如果这时被关闭了,拒绝任务
reject(command);
else if (workerCountOf(recheck) == 0) //如果之前的线程已被销毁完,新建一个线程
addWorker(null, false);
}
//3.核心池已满,队列已满,试着创建一个新线程
else if (!addWorker(command, false))
reject(command); //如果创建新线程失败了,说明线程池被关闭或者线程池完全满了,拒绝任务
}
流程图
2. 构造器的作用
- corePoolSize:核心线程池数量
- 在线程数少于核心数量时,有新任务进来就新建一个线程,即使有的线程没事干
- 等超出核心数量后,就不会新建线程了,空闲的线程就得去任务队列里取任务执行
- maximumPoolSize: 最大线程数量
- 包含核心线程池数量+核心以为的数量
- 如果任务队列满了,并且池中线程数小于最大线程数,会再创建新的线程执行任务
keepAliveTime
:核心池以外的线程存活时间,即没有任务的外包的存活时间- 如果给线程池设置
allowCoreThreadTimeOut(true)
,则核心线程在空闲时头上也会响起死亡的倒计时 - 如果任务是多而容易执行的,可以调大这个参数,那样线程就可以在存活的时间里有更大可能接受新任务
- 如果给线程池设置
workQueue
:保存待执行任务的阻塞队列- ArrayBlockingQueue:基于数组、有界,按 FIFO(先进先出)原则对元素进行排序
- LinkedBlockingQueue:基于链表,按FIFO (先进先出) 排序元素
- 吞吐量通常要高于 ArrayBlockingQueue
- Executors.newFixedThreadPool() 使用了这个队列
- SynchronousQueue:不存储元素的阻塞队列
- 每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态
- 吞吐量通常要高于 LinkedBlockingQueue
- Executors.newCachedThreadPool使用了这个队列
- PriorityBlockingQueue:具有优先级的、无限阻塞队列
threadFactory
:每个线程创建的地方- 可以给线程起个好听的名字,设置个优先级啥的
- handler:饱和策略,大家都很忙,咋办呢,有四种策略
- CallerRunsPolicy:只要线程池没关闭,就直接用调用者所在线程来运行任务
- AbortPolicy:直接抛出 RejectedExecutionException 异
- DiscardPolicy:悄悄把任务放生,不做了
- DiscardOldestPolicy:把队列里待最久的那个任务扔了,然后再调用 execute() 试试看能行不
- 我们也可以实现自己的 RejectedExecutionHandler 接口自定义策略,比如如记录日志什么的
赞助