线程池创建的方式:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
RejectedExecutionHandler handler)
参数的介绍:
corePoolSize:线程池核心线程数
maximumPoolSize:线程池最大线程数
keepAliveTime:非核心线程数,空闲多久会被回收
unit:空闲时间的单位
workQueue:线程池等待队列的大小
handler:线程池达到饱和的策略处理类
线程池原理:
先看下面一张流程图:
1.当一个任务过来时,先判断当前线程池的核心线程数是否已经创建满了
如果没有满,直接创建线程,进行任务处理;如果满了,进入到第二步
注意:这里即使当前创建过的线程有空闲的,依然会重新创建线程执行任务,而不是拿空闲线程来用。
2.判断当前的线程池队列是否满了,没有满,任务加入到队列中进行排队;如果满了,进入到第三步
3.判断当前线程池行程数量是否达到了最大线程数,如果没有达到,创建线程执行当前任务,如果达到了,进入第4步处理
4.按照配置的饱和处理策略,进行处理
JDK1.8源码解析:
先看ThreadPoolExecutor的内部状态实现方式:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
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; }
我们可以看到这里有一个AtomicInteger类型的ctl 变量:int一共是32位,这里采用的是低位29位表示线程池线程的个数,高3位表示当前线程池的状态。
COUNT_BITS:我们看这个常量,它的值=32-3=29
RUNNING = -1 << COUNT_BITS; 这里表示高3位是111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
SHUTDOWN = 0 << COUNT_BITS;这里表示高3位是000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
STOP = 1 << COUNT_BITS;这里表示高3位是001,该状态的线程池不会接收新任务,也不会处理阻塞队列中的任务,并且会中断正在运行的任务;
TIDYING = 2 << COUNT_BITS;这里表示高3位是010,表示当前线程池所有任务都已经终止了;
TERMINATED = 3 << COUNT_BITS;这里表示高3位是011,表示erminated()
方法已经执行完成 ;
运行流程解析:
1.ThreadPoolExecutor的execute方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//这里获取当前工作线程的个数 跟核心线程数对比
//如果小于核心线程数 进入到内部
//否者 继续往下走
int c = ctl.get();//这里是获取当前线程池的状态
if (workerCountOf(c) < corePoolSize) {
//创建线程执行当前任务 如果成功 直接结束方法 否者继续往下走
if (addWorker(command, true))
return;
c = ctl.get();
}
//这里需要判断当前线程池的状态=RUNNING,并且把当前任务加入到任务队列里
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//如果线程池不是RUNNING状态,成功从阻塞队列中删除任务,执行reject方法处理任务
if (! isRunning(recheck) && remove(command))
reject(command);
//线程池处于running状态,但是没有线程,则创建线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//队列也满了,这个时候会创建非核心线程去执行任务
//如果失败,直接走饱和拒绝策略处理
else if (!addWorker(command, false))
reject(command);
}
注意:这里做了双重检查,是因为多线程环境下,线程池的状态是随时变化的,而获取状态跟下面操作是非原子性的,如果不做双层check,可能会出现线程变成了非运行状态,结果却把任务放到了执行任务队列里,导致任务无法执行。且不会通知到调用端。
2.addwork()方法:主要是创建线程并执行任务
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//如果当前线程池状态是非运行状态并且
//状态是shutdown状态而且任务不为空,队列是空的 直接return失败
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//获取当前运行中的线程数
int wc = workerCountOf(c);
//线程数跟配置大小作比较
//如果大于等于最大数 或者 通过传入的参数 来跟核心线程数和最大线程数做比较 当前线程数超过配置值时 直接return失败
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//通过CAS来增加当前线程池里线程的数量 修改成功 直接跳出当前循环
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//创建一个Worker对象,传入当前的任务
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//这里加入锁机制 获取重入锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
//获取状态
int rs = runStateOf(ctl.get());
//如果当前状态是运行中 或者 (是shutdown 并且任务为空的话)
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//如果当前线程是已经运行中的 抛出异常
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//一切正常 把当前worker加入到set中
workers.add(w);
//如果set的大小大于最大线程大小的时候 扩大当前值
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
//释放锁
mainLock.unlock();
}
//任务加入到worker成功 执行当前线程
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
注意:这里在添加到set的时候,做了加锁处理,这里主要是需要准确判断当前线程池的状态,在此处执行的时候,不容许其他线程修改当前线程池的状态,已达到正确性。
3.worker.runWorker()方法,这个是执行任务的核心方法:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
//这里是修改worker的状态 容许当前任务被线程池打断
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//当前需要执行的任务不为空
//这里用的是个while 如果传入的任务为空的话
//会从线程池队列里拿出任务来处理
while (task != null || (task = getTask()) != null) {
//加锁 在执行的时候 不容许被其他线程打断
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
//判断当前线程池的状态,如果是中断状态,设置当前线程中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
整体流程为:
1.获取当前任务;
2.线程启动之后,通过unlock方法释放锁,设置AQS的state为0,表示运行可中断;
3.执行传入的任务,或者是直接从线程池的任务队列里面获取任务执行;
4.给当前线程加锁,保证不会被其他前程中断(如果线程池直接中断,这里也会中断);
5.检查当前线程池的状态,如果当前线程池是中断状态,这直接中断当前线程;
6.执行beforeExecute方法
7.执行当前任务的run方法
8.执行afterExecute方法
9.解锁 容许被其他线程中断
4.我们看一下从任务队列里面获取任务的方法:getTask()
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//这里还是需要判断当前线程池的状态
//如果是非运行状态,并且是(中断正状态或者是任务队列为空)
//返回空
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
//查看当前运行中状态的线程个数
int wc = workerCountOf(c);
// Are workers subject to culling?
//如果配置了容许线程会被回收 或者 当前线程数大于核心线程数
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//如果当前线程数大于最大线程数 或者是达到了回收的条件
//而且线程数量大于1 或者 任务队列已经空了
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//如果线程不允许无休止空闲timed == true, workQueue.poll任务:如果在keepAliveTime时间内,阻塞队列还是没有任务,则返回null
//如果线程允许空闲等待而不被销毁timed == false,workQueue.take任务:如果阻塞队列为空,当前线程会被挂起等待;当队列中有任务加入时,线程被唤醒,take方法返回任务,并执行;
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}