很早之前我就写过两篇关于线程池的博客, 但是这两篇博客的认识比较浅陋, 有很多需要订正的地方, 所以我决定新开一篇博客.
https://blog.csdn.net/leisurelen/article/details/107872827
https://blog.csdn.net/leisurelen/article/details/107872827
在这里我先简单描述下线程池的工作流程
线程池有7个参数, 每个参数都有各自的作用.
其实按照代码的逻辑, 这个也可以叫做 正常工作线程数
, 线程池启动工作后, 首先 也就是在线程池的任务队列够用
的时候, 会始终以这个数量的线程工作.
当任务队列不够用的时候, 会继续增加超出 corePoolSize的数量的线程, 但是最终线程不会超出maximumPoolSize数量. 设置这个值可以防止任务太多而创建太多的线程
当任务较少的时候线程空闲的时候, 超出核心线程数量的线程会存活的时间, 超出这个时间后会自动关闭多余的线程.
线程池往往是用来批量处理任务的, 当任务过多来不及处理的时候, 会将任务放入这个队列中缓存, 避免阻塞线程池的调用方法
线程池中的线程的创建都是通过这个工厂的, 在这里可以定义线程的名称, 方便在线程出现问题的时候在错误日志中一眼就能看到具体的线程.
这个是最容易被忽略的方法, 当线程达到上限, 并且任务队列也满了的时候, 如果继续往里面添加任务, 就会直接执行拒绝策略中的代码. 默认的拒绝策略是 直接抛出异常 .
如果线程池的状态是shutdown , 加入任务也会执行拒绝策略
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,
20,
1,
TimeUnit.MINUTES,
new SynchronousQueue<>(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 30; i++) {
threadPoolExecutor.execute(()->{
try {
Thread.sleep(1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
代码的每一行我都加上了注释, 看注释就能明白.
简单总结下流程就是:
/**
* Executes the given task sometime in the future. The task
* may execute in a new thread or in an existing pooled thread.
*
* 在将来的摸一个执行传入的任务, 任务会被新的线程或者池中的线程执行
*
* If the task cannot be submitted for execution, either because this
* executor has been shutdown or because its capacity has been reached,
* the task is handled by the current {@code RejectedExecutionHandler}.
* 如果任务不能被提交或者执行, 是因为这个线程池被关闭了, 或者容量已经满了, 这个线程被拒绝策略执行了
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution
* @throws NullPointerException if {@code command} is null
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
* 流程分3步
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
* 1. 如果正在运行的线程数少于核心线程数, 会尝试启动一个新线程来运行给定的任务,
* 调用addWorker方法会先检查运行状态和运行的线程数了, 如果状态不对会返回false阻止这个任务的执行
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
//检查是否达到核心线程数
if (workerCountOf(c) < corePoolSize) {
//调用addWorker新增线程,执行任务
if (addWorker(command, true))
return;
c = ctl.get();
}
// 检查运行状态, 并尝试放入任务队列中
if (isRunning(c) && workQueue.offer(command)) {
//放入成功了后, 再次检查运行状态
int recheck = ctl.get();
//如果运行状态不对, 会从队列中移除这个任务
if (! isRunning(recheck) && remove(command))
//状态不对, 移除成功后, 会执行拒绝策略
reject(command);
else if (workerCountOf(recheck) == 0)
//检查运行的线程数, 如果为0, 会直接启动一个线程
addWorker(null, false);
}
//如果非运行状态, 或者放入任务队列失败, 会尝试启动一个新的线程来运行任务
else if (!addWorker(command, false))
//启动新的线程运行任务失败, 执行拒绝策略
reject(command);
}
内部代码分上下两块,
可以看到, 加入任务的两个模块都没有直接涉及到任务队列的交互, 只是简单的判断状态, 创建线程启动等, 更重要的逻辑其实在Worker对象内部,
/**
* Checks if a new worker can be added with respect to current
* pool state and the given bound (either core or maximum). If so,
* the worker count is adjusted accordingly, and, if possible, a
* new worker is created and started, running firstTask as its
* first task. This method returns false if the pool is stopped or
* eligible to shut down. It also returns false if the thread
* factory fails to create a thread when asked. If the thread
* creation fails, either due to the thread factory returning
* null, or due to an exception (typically OutOfMemoryError in
* Thread.start()), we roll back cleanly.
*
* @param firstTask the task the new thread should run first (or
* null if none). Workers are created with an initial first task
* (in method execute()) to bypass queuing when there are fewer
* than corePoolSize threads (in which case we always start one),
* or when the queue is full (in which case we must bypass queue).
* Initially idle threads are usually created via
* prestartCoreThread or to replace other dying workers.
*
* @param core if true use corePoolSize as bound, else
* maximumPoolSize. (A boolean indicator is used here rather than a
* value to ensure reads of fresh values after checking other pool
* state).
* @return true if successful
*/
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, 任务队列是否为空等状态是否正常,
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty())
)
return false;
//循环更新状态, 和线程数量的更新, 都是使用CAS的模式更新
for (;;) {
int wc = workerCountOf(c);
//检查运行的线程数是否超标,
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//CAS方式更新线程数量
if (compareAndIncrementWorkerCount(c))
//更新成功后直接跳转到方法第一行, 并且不在进入这些for循环中,
//因为状态啥的已经更新成功了
break retry;
c = ctl.get(); // Re-read ctl
//如果cas增加线程数失败, 检查下状态是否还是和之前一样,
if (runStateOf(c) != rs)
//不一样了, 会跳转到第一行, 并会重新进入for循环中走一遍流程
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());
//二次检查池的状态
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//池的状态没有问题, 检查线程是否存活
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//worker放入池的hashset中
workers.add(w);
int s = workers.size();
//并将workers的数量赋予池中的largestPoolSize成员变量
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//worker创建成功并加入workers成功,
if (workerAdded) {
//启动线程,
t.start();
// 修改状态
workerStarted = true;
}
}
} finally {
if (! workerStarted)
//启动线程失败, 进行失败处理
addWorkerFailed(w);
}
return workerStarted;
}
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
// 直接设置AQS的状态,
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
//下面的省略
}
/**
* Main worker run loop. Repeatedly gets tasks from queue and
* executes them, while coping with a number of issues:
*
* 1. We may start out with an initial task, in which case we
* don't need to get the first one. Otherwise, as long as pool is
* running, we get tasks from getTask. If it returns null then the
* worker exits due to changed pool state or configuration
* parameters. Other exits result from exception throws in
* external code, in which case completedAbruptly holds, which
* usually leads processWorkerExit to replace this thread.
*
* 2. Before running any task, the lock is acquired to prevent
* other pool interrupts while the task is executing, and then we
* ensure that unless pool is stopping, this thread does not have
* its interrupt set.
*
* 3. Each task run is preceded by a call to beforeExecute, which
* might throw an exception, in which case we cause thread to die
* (breaking loop with completedAbruptly true) without processing
* the task.
*
* 4. Assuming beforeExecute completes normally, we run the task,
* gathering any of its thrown exceptions to send to afterExecute.
* We separately handle RuntimeException, Error (both of which the
* specs guarantee that we trap) and arbitrary Throwables.
* Because we cannot rethrow Throwables within Runnable.run, we
* wrap them within Errors on the way out (to the thread's
* UncaughtExceptionHandler). Any thrown exception also
* conservatively causes thread to die.
*
* 5. After task.run completes, we call afterExecute, which may
* also throw an exception, which will also cause thread to
* die. According to JLS Sec 14.20, this exception is the one that
* will be in effect even if task.run throws.
*
* The net effect of the exception mechanics is that afterExecute
* and the thread's UncaughtExceptionHandler have as accurate
* information as we can provide about any problems encountered by
* user code.
*
* @param w the worker
*/
final void runWorker(Worker w) {
//获取当前线程
Thread wt = Thread.currentThread();
//获取当前任务
Runnable task = w.firstTask;
w.firstTask = null;
//先unlock , 同样也可以加锁, 别的线程同样无法占用资源. 具体逻辑可以看下java的锁逻辑
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//while中判断task是否为空, 如果为空则从getTask()中取任务, getTask就是从任务队列中取任务.
// 没取到任务, 不会进入循环, 也就还是占用着锁
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 {
// 这是一个空实现, 并且是protect的方法, 可以继承实现这个方法, 从而在线程池任务执行前加上自己的逻辑
//注意, 如果这里抛出异常, 会导致下面的业务run方法无法执行, 也是一种拦截线程池执行的方式
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 {
//也是空实现, 并且是protect的方法, 可以继承实现这个方法, 从而在线程池任务执行结束后加上自己的逻辑
afterExecute(task, thrown);
}
} finally {
// 把当前处理完的任务设置成null , 方便垃圾回收
task = null;
// 计数加1
w.completedTasks++;
// 继续调用unlock方法, 加上锁, 这会使得线程在空闲状态下都是锁定状态
w.unlock();
}
}
//处理完成后回复标识
completedAbruptly = false;
} finally {
//处理线程的退出逻辑, allowCoreThreadTimeOut 内部会根据这个变量值来判断是否保持核心线程数量
processWorkerExit(w, completedAbruptly);
}
}
父类中的submit方法支持接受Callable方法, 并且返回一个future.
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
看完整个流程, 知道一个任务加入线程池, 线程池是怎样新建线程, 怎样执行任务的,
但是还是有一个疑惑, 线程池没有任务的时候, 线程是如何保持存活的, 非核心线程是如何保证存活一段时间后关闭的?
答案: 在下图中, 这个while循环在getTask能持续取得任务的时候, 这个线程不会结束, 如果没有任务后, 线程就会结束; 但是还是没有对核心线程和非核心线程进行区分对待. 其实逻辑是在getTask中, 如果是核心线程getTask会永远阻塞的, 非核心线程才会阻塞一段时间后返回null . 返回null 这个循环就会结束, 线程就会结束, 具体代码在第二张图中