线程池底层原理
线程池执行流程及原理解析
线程模型
用户线程(UTL):由应用去管理线程,不需要用户态-内核态切换。
内核线程(KTL):创建线程在任务管理器中可见,java创建的线程由操作系统管理,操作系统对应一个内核空间线程,线程和内核线程一一对应。
java的线程是KTL内核线程模型。关键代码,Thread类中创建线程,是由本地方法库中的start0方法创建线程。
线程状态:
private final AtomicInteger ctl = new AtomicInteger(RUNNING);
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1; // 2^29
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS; // 111
private static final int SHUTDOWN = 0 << COUNT_BITS; // 000
private static final int STOP = 1 << COUNT_BITS; // 001
private static final int TIDYING = 2 << COUNT_BITS; // 010
private static final int TERMINATED = 3 << COUNT_BITS; // 011
private static int runStateOf(int c) { return c & ~CAPACITY; } // 获取当前状态
private static int workerCountOf(int c) { return c & CAPACITY; } // 活动线程的数量
private static int ctlOf(int rs, int wc) { return rs | wc; }
// 运算结果(高3位为状态、低29位为数量)
COUNT_BITS:29
CAPACITY :000 11111111111111111111111111111
RUNNING :111 00000000000000000000000000000
SHUTDOWN :000 00000000000000000000000000000
STOP :001 00000000000000000000000000000
TIDYING :010 00000000000000000000000000000
TERMINATED:011 00000000000000000000000000000
ctl:记录活动线程的数量(低29位)、线程池的状态(高3位)【Integer.SIZE共32位】
CAPACITY=0 :初始容量
COUNT_BITS:29 =(Integer.SIZE=32)-3,
RUNNING(111):接受新任务、可以处理已添加的任务。
SHUTDOWN(000):不接受新任务、可以处理已添加的任务。
STOP(001):不接受新任务、不处理已添加的任务、并且中断正在处理的任务。
TIDYING(010):所有的任务已经终止,ctl数=0。
TERMINATED(011):线程池终止
构造函数解析
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- acc : 获取调用上下文
- corePoolSize: 核心线程数量,规定线程池有几个线程(worker)在运行。
- maximumPoolSize: 最大的线程数量。当workQueue满了,不能添加任务的时候,这个参数才会生效。规定线程池中最多有多少个线程(worker)在执行
- workQueue:存放任务的队列。
- unit: 生存时间的单位
- keepAliveTime:超出corePoolSize大小的那些线程的生存时间,这些线程如果长时间没有执行任务并且超过了keepAliveTime设定的时间,就会消亡。
- threadFactory: 创建线程的工厂,在这个地方可以统一处理创建的线程的属性。
- handler:当workQueue已经满了,并且线程池线程数已经达到maximumPoolSize,将执行拒绝策略。
线程池执行流程
// 执行任务方法
public void execute(Runnable command) {
// 1、小于核心线程数,直接创建线程执行任务addWorker(command, true)
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2、大于核心线程数,尝试加入队列,进行双重检测线程池运行状态
// 创建非核心线程执行任务addWorker(null, false)
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、尝试入队失败,addWorker(command, false)在自旋时状态检测返回false,未创建Worker对象
// 则执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
// 添加worker对象到workers集合中
private boolean addWorker(Runnable firstTask, boolean core) {
//
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 1、检测线程池的状态,如果关闭了则不再添加worker,直接返回false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 2、自旋的方式对活动的线程数workercount+1
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 3、封装worker对象,内部创建了新的线程,w.thread即可获取该线程
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
// 4、通过线程池的重入锁机制,将worker加入workers集合中,等待workers执行。
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 5、加入成功后,调用该线程执行。
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
// 加入线程池失败,则内部通过自旋的方式,将活动线程数workerCount-1
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
Worker(Runnable firstTask) {
setState(-1);
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this); // 1、此处将worker对象本身加入线程
}
public void run() { // 2、由于worker对象实现Runnable,由1处的thread执行start时,会调用该方法
runWorker(this); // 3、该runWorker方法为线程池的runWorker方法。
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
// 4、此处只留下了关键代码,如果firstTask==null,则通过getTask方法从队列中获取任务。
while (task != null || (task = getTask()) != null) {
// 5、执行任务
task.run();
}
}
线程集合:HashSet:workers :不断的从workQueue队列中获取线程,执行任务
阻塞队列:BlockingQueue:workQueue:存放线程任务的队列,FIFO一端入队一端出队
流程原理:
1、用户向线程池中提交线程任务,执行execute方法。
2、如果当前线程池中的线程数量workerCount小于核心线程数corepoolsize
2.1、通过自旋的方式对workerCount数量做CAS进行+1,如果当前活动线程>corepoolsize或者maxinumpoolsize,则自旋失败,返回false,如果成功,则开始封装Worker对象
2.2、通过线程任务封装Worker对象时,先获取线程池的重入锁,获取锁后判断当前线程池的状态,如果为001、010、011状态,则操作失败,会对workerCount数量CAS做-1,同时停止线程池,返回false。
2.3、线程池将该线程封装成Worker对象,添加到workers执行。
3、如果当前线程池中的线程数量workerCount达到了corepoolsize,则将任务加入workQueue队列中。
4、如果队列已经满,但未达到maxinumpoolsize数量,新建(非核心)线程执行任务。
4.1、由addWorker(command, false),添加的firstTask为null,所以封装的Worker对象的firstTask为null,
4.2、由Worker对象执行线程,由于firstTask==null,所以不断循环的从workQueue队列中获取任务步骤3加入的任务执行。
4.3、执行完成后对该任务标记completedTaskCount已完成数量。同时移除该任务。
5、如果队列已经满,总线程数达到了maxinumpoolsize数量,会由RejectedExecutionHandler执行拒绝策略。
默认线程池:
newFixedThreadPool:指定线程的线程池,核心数=最大数,不会释放线程
newCachedThreadPool:可缓存60秒线程的线程池,核心数=0,最大数=Integer.MAX会自动释放线程
newSingleThreadExecutor:只有一个线程,核心数=最大数=1,可以保证线程的任务顺序执行
newScheduledThreadPool:可以指定时间、周期性执行提交的任务线程
默认任务队列:
1、ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
2、LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
3、SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
4、PriorityBlockingQuene:具有优先级的无界阻塞队列;
默认拒绝策略:
1、AbortPolicy:直接抛出异常,默认策略;
2、CallerRunsPolicy:用调用者所在的线程来执行任务;
3、DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
4、DiscardPolicy:直接丢弃任务;
当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。