- 日常所说的“核心线程”、“非核心线程”是一个虚拟的概念,是为了方便描述而虚拟出来的概念
- 在代码中并没有标记哪些线程为“核心线程”或者“非核心线程”。所有线程都是一样的。
线程池是如何实现的?
在Java中,线程池中所谓的“线程”,其实就是一个静态内部类Worker
,它是基于AQS
实现的,并实现Runnable
。存放在线程池的HashSet<Worker> workers
成员变量中。
而需要执行的任务,则放在BlockingQueue<Runnable> workQueue
中。这样,整个线程实现的基础思想就是:从workerQueue中获取任务,放在worker中进行处理。
静态内部类Worker:
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {......}
线程池的核心参数
corePoolSize
核心线程数量maximunPoolSize
最大线程数量 ,一般 maximunPoolSize > corePoolSizekeepAliveTime
空闲线程的存活时间- 存活时间单位
workQueue
任务队列(共计5种,按照不同的业务场景使用不同的队列)- RejectedExecutionHandler 拒绝策列
线程池的线程会在何时创建与销毁
创建
首先,线程池在创建之初并不会创建线程,当任务来临后才会创建第一个线程,每次创建线程都是做出如下判断:
- 如果正在运行的线程数量 <
corePoolSize
,那么马上创建线程运行这个任务; - 如果正在运行的线程数量 >= corePoolSize,那么将这个任务放入队列;
- 如果这时候队列满了,而且正在运行的线程数量 < maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
- 如果队列满了,而且正在运行的线程数量 >= 于maximumPoolSize,那么线程池会抛出异常RejectExecutionException。
销毁
当空闲线程大于空闲存活时间,就会销毁掉。直到线程数量等于核心线程数。剩下的线程就是核心线程。
核心线程是否能销毁
ThreadPoolExecutor
中存在一个参数allowCoreThreadTimeOut
,此参数表示是否允许核心线程超时,默认为 false,所以一般情况下,是不会销毁的。
如果调整参数,是可以做到销毁核心线程的。
核心方法- getWork() 获取任务方法
当线程完成当前任务后,会进入死循环,不断的尝试获取任务。
- 如果 生存时间 >
keepAliveTime
,会返回 null - 如果 生存时间 <
keepAliveTime
,会阻塞获取
// 线程池源码,获取任务
// 为分析而简化后的代码
private Runnable getTask() {
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int wc = workerCountOf(c);
// timed变量用于判断是否需要进行超时控制。
// allowCoreThreadTimeOut默认是false,也就是核心线程不允许进行超时;
// wc > corePoolSize,表示当前线程池中的线程数量大于核心线程数量;
// 对于超过核心线程数量的这些线程,需要进行超时控制
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if (timed && timedOut) {
// 如果需要进行超时控制,且上次从缓存队列中获取任务时发生了超时,那么尝试将workerCount减1,即当前活动线程数减1,
// 如果减1成功,则返回null,这就意味着runWorker()方法中的while循环会被退出,其对应的线程就要销毁了,也就是线程池中少了一个线程了
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
// 注意workQueue中的poll()方法与take()方法的区别
//poll方式取任务的特点是从缓存队列中取任务,最长等待keepAliveTime的时长,取不到返回null
//take方式取任务的特点是从缓存队列中取任务,若队列为空,则进入阻塞状态,直到能取出对象为止
if (r != null) return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
线程池的拒绝策略
在添加工作任务时,工作队列满后,会执行拒绝策略(都是静态内部类)
- AbortPolicy:直接抛出拒绝异常,默认项
- CallerRunsPolicy:由线程池的调用者线程去执行多余的方法,串行执行。场景:所有任务都要执行。
- DiscardOledestPolicy:放弃等待队列中最老的数据,迎接新的数据
- DiscardPolicy:直接无视,不做任何处理。不添加队列,不报错,不做标识
逻辑:查看 ThreadPoolExcutor.java
文件中 public void execute(Runnable var1) {......}
方法