最近仔细学习了一下java 线程池的源码,这里来一起分享下,如果有错误的地方,欢迎指正!
我们将会从一下六个方面介绍:
二、构造参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
不同类型的线程池最终都会调用这个构造函数
三、常用的线程池
1.FixedSizeThreadPool
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(),
threadFactory);
}
可以看到corePoolSize和maximumPoolSize均为nThreads参数,并且没有超时且队列是没有边界的。所以该线程池一旦开启,最多会有 nThreads个线程,且线程一旦创建,就不会销毁。只要有任务提交,就会添加给核心线程或加入队列。
2.SingleThreadPool
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
SingleThreadPool创建一个Worker线程操作一个无边界的队列。如果使用该线程池,那么所有提交的任务将会按照顺序被执行。
3.CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
CachedThreadPool只要需要新线程就会创建线程,如果之前创建的线程还可以复用,那么就会复用之前的线程。
4.ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
ScheduledThreadPool核心线程数量固定,非核心线程数量为Integer.MAX_VALUE,该线程池主要用于执行周期性的任务或在延时一段时间后执行任务。
四、常见的拒绝策略
CallerRunsPolicy
在当前线程执行
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down, in which case the task
* is discarded.
*/
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
AbortPolicy
直接抛异常 RejectedExecutionException
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecutionException}.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
DiscardPolicy
直接丢弃拒绝线程
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
DiscardOldestPolicy
丢弃一个未被处理的最久的线程,然后重试
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements
RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
五、线程池的状态
状态 | 描述 |
---|---|
RUNNING=111 | 线程池正常运行,可以接受新的任务并处理队 列中的任务 |
SHUTDOWN=000 | 关闭状态,不再接受新的任务,但是会执行队 列中的任务。在线程池处于 RUNNING 状态 时,调用 shutdown()方法会使线程池进入到 该状态。(finalize() 方法在执行过程中也会 调用shutdown()方法进入该状态) |
STOP=001 | 不再接受新任务,不处理队列中的任务,中断 进行中的任务。在线程池处于 RUNNING 或 SHUTDOWN 状态时,调用 shutdownNow() 方法会使线程池进入到该状 态 |
TIDYING=010 | 所有任务已经终止,workerCount为0,线程 状态转换到TIDYING,线程池进入该状态后 会调用 terminated() 方法进入 TERMINATED 状态 |
TERMINATED=011 | terminate()函数执行完成后进入该状态。 |
转换关系如下图:
如下代码中 有一个控制变量ctl (初始化值为线程数0,状态为RUNNING)
ctl变量是整个类的核心,AtomicInteger保证了对这个变量的操作是原子的,保证多线程同步问题,用这 个变量保存了两个内容:
AtomicInteger是一个32位的整数,为了将状态和数量放在一起,所以高3位用于表示表示状态,低29位表示数量
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; }
/*
* Bit field accessors that don't require unpacking ctl.
* These depend on the bit layout and on workerCount being never negative.
*/
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
六、执行流程 (重点)
下面我们以 execute 为入口来阅读 ThreadPoolExecutor 是如何执行给定的任务:
/**
* 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:
*
* 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.
*
* 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();
// core thread
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// work queue
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);
}
// 非核心线程
else if (!addWorker(command, false))
reject(command);
}
其实注释中已经写的很详细了,execute 会分为三步来执行(这里就用到了ctl 变量来获取worker的数量)
流程图如下:
addWorker的执行过程:
/**
* 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.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
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;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
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();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
首先是两个死循环,外循环主要检查线程池运行状态,内循环检查workerCount之后再检查运行状态。下面简单分析一下哪些情况下才可以进入到 内循环,否则就直接返回false了。下面是可以进入到内循环的情况:
(1)rs>=SHUTDOWN为false,即线程池处于RUNNING状态
(2)rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()这个条件为true,也就意味着三个条件同时满足,即线程池状态为 SHUTDOWN且firstTask为null且队列不为空,这种情况为处理队列中剩余任务。上面提到过当处于SHUTDOWN状态时,不接受新任务,但是会 处理完队列里面的任务。如果firstTask不为null,那么就属于添加新任务;如果firstTask为null,并且队列为空,那么就不需要再处理了。 当进入到内循环后,会首先获取当前运行的线程数量。首先判断当前运行线程数量是否大于等于CAPACITYA(2^29-1),其次根据是否是核心线程 与corePoolSize或者maximumPoolSize比较。所以线程的数量不会超过CAPACITY和maximumPoolSize的较小值。如果数量符合条件,那么就让 ctl加1,然后跳出外部循环。如果线程数量达到了最大,那么再判断当前状态,如果状态和之前的不一致了,那么继续外循环。
下面是可以跳出外 循环的情况:
(1)如果是核心线程,当前线程数量小于CAPACITY和corePoolSize中的较小值
(2)如果是非核心线程,当前线程数量小于CAPACITY和maximumPoolSize中的较小值。 一旦跳出外循环,表示可以创建创建线程,这里具体是Worker对象,Worker实现了Runnable接口并且继承AbstractQueueSynchronizer(AQS的东西请看下一篇《AQS源码分析》),内部 维持一个Runnbale的队列。try块中主要就是创建Worker对象,然后将其保存到workers中,workers是一个HashSet,表示工作线程的集合。然 后如果添加成功,则开启Worker所在的线程。如果开启线程失败,则调用addWorkerFailed方法,addWokerFailed用于回滚worker线程的创建
这里会对 workers.add(w); 操作进行加锁,t.start() 之后会调用 runWorker (这里如果想不明白可以去看下Worker的构造方法)
runWorker执行的过程:
/**
* 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;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) { //一个线程处理多个task,也就是线程的重复利用
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);
}
}
runWoker方法主要不断从队列中取得任务并执行。首先获得Worker所在的线程,在addWorker中获得Worker的Thread变量并调用start方法, 所以Worker是运行在Worker的Thread中,而thread变量是通过threadFactory创建的。可以看到首先获取Worker的firstTask对象,该对象有可 能为空,初始时对于核心线程不为空,但是对于非核心线程就为空,下面是一个循环,跳出循环的条件为task==null&&(task=getTask()) ==null,也就是说当没有任何任务的时候,就跳出循环了,跳出循环也就意味着Worker的run方法执行结束,也就意味着线程结束;否则会一直尝 试着从队列中获取任务来执行,getTask会阻塞,一旦获取到任务,就对Worker加锁,然后判断状态,如果状态处于STOP状态及之上,就不处理 任务了;否则处理任务,在处理任务之前,首先会调用beforeExecute,然后调用Runnbale方法的run方法,最后调用afterExecute,其中 beforeExecute和afterExecute都是空实现,继承ThreadPoolExecutor时可以实现,在每个任务运行之前和之后做一些处理工作。一旦一个任务执 行完毕后,将task置为null,然后继续尝试从队列中取出任务
流程图如下:
getTask相关流程:
/**
* Performs blocking or timed wait for a task, depending on
* current configuration settings, or returns null if this worker
* must exit because of any of:
* 1. There are more than maximumPoolSize workers (due to
* a call to setMaximumPoolSize).
* 2. The pool is stopped.
* 3. The pool is shutdown and the queue is empty.
* 4. This worker timed out waiting for a task, and timed-out
* workers are subject to termination (that is,
* {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
* both before and after the timed wait, and if the queue is
* non-empty, this worker is not the last thread in the pool.
*
* @return task, or null if the worker must exit, in which case
* workerCount is decremented
*/
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;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
getTask从队列中取出任务;但是在以下几种情况下会返回null,上面说过如果返回null也就标识了runWorker中循环跳出,上面说过,runWorker 中的循环跳出意味着Worker线程执行完毕会回收,所以调用了decrementWorkerCount将Worker数量减1。下面返回null的情况 :
(1)由于调用了setMaximumPoolSize导致Worker的数量超过maximumPoolSize
(2)线程池处于STOP状态,STOP状态不再处理队列中的任务
(3)线程池处于SHUTDOWN并且queue为空。SHUTDOWN状态仍然处理已经在队列中的任务,但是如果queue为空,自然就不再处理了
(4)Worker在等待队列时超时
getTask内部依然是一个死循环,首先依然是判断状态,如果状态是STOP及以上,那么返回null;如果状态是SHUTDOWN且队列为空,那么也返 回null。这对应于情况2和3。 接下来是比较Worker的数量,首先获取Worker的数量以及是否需要超时标志,如果设置了allCoreThreadTimeOut为true,那么就意味着所以线 程都得检验超时;而如果没有设置为true,那么只需要在Worker数量超过corePoolSize时检查超时。接下来是判断数量是否超过 maximumPoolSize,如果超过了,则需要结束多余的Worker;如果超时了并且有时间限制,也需要停止线程。如果没有进入到if语句中,那么将 会尝试从队列中获取任务,如果需要有时间限制,那么就调用workQueue的poll方法,如果没有则调用take方法,如果可以从队列中取到任务,那 么就返回任务交由runWorker中去执行;但是如果返回失败,那么需要设置timeOut为true,这样在下一次进入循环时,会清除一个Worker
最终会调用 processWorkerExit 为死亡的worker做清理和记录
ps:给大家推荐个特别好的juc课程:https://edu.csdn.net/course/detail/22039