接上文
前面介绍了下线程池的使用方法,传送门:java线程池介绍,这里看下具体的源码
回顾下核心参数
流程
线程池的构造方法
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- corePoolSize:线程池的核心线程数
- maximumPoolSize:最大线程数,如果任务过多,超过核心线程数的处理能力,就会创建新的线程执行任务,这个参数,代表所能创建的最大线程的个数。
- keepAliveTime:线程存活时间。在上面的情况中,如果创建的线程数量超过了核心线程数,并且在keepAliveTime时间段内,没有新的任务执行,就回把多余的线程进行回收。
- unit:时间单位。
- workQueue:任务队列。如果线程中的线程处理任务繁忙,此时任务就将堆积到任务队列里面,线程处理时,从队列里面取出执行
- RejectedExecutionHandler:拒绝策略。如果使用了有界队列,任务达到了最大值,再有任务到达时,该怎么做呢?这时就涉及到拒绝队列
从默认线程的创建出发
默认的线程池创建
@Test
public void cachedThreadPoolTest() {
// 可缓存的线程池
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello world");
}
});
}
这是jdk提供的一种默认线程池,那么返回的ExecutorService是会什么呢?
ExecutorService接口继承了Executor接口,Executor接口里面只有一个方法execute,参数是Runnable接口,说明这个方法是真正执行任务的。
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
那么既然有了execute方法,为什么还要定义一个ExecutorService接口呢?主要是ExecutorService实现了很多扩展功能,比如常用的提交任务submit(),关闭任务提交shutdown(),检测任务是否被关闭isShutdown()等
看下类图
当得到了创建的线程池后,提交了任务,任务怎么执行的呢?
任务执行
由上面的流程图可知,提交一个任务执行到线程池执行,如果核心线程未满,将直接获取一个线程执行任务。
也就是说,如果如果刚创建了一个线程池,在核心线程未满的情况下,都会新建一个线程来执行任务,我们看下代码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();
}
// 如果线程都在处理任务,将新任务放入任务队列成功
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);
}
整个流程就是前面流程图所示。
重点看下添加任务线程,addWorker
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;
// CAS方式递增线程计数器,+1
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;
ThreadPoolExecutor.Worker w = null;
try {
w = new ThreadPoolExecutor.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;
}
看下大致逻辑:
- 方法2个参数,addWorker(Runnable firstTask, boolean core):firstTask表示,新线程是否先运行的任务;core表示是否核心线程,如果为false,表示非核心线程
- 如果线程池不是运行状态,则不创建新线程
- CAS的方式递增线程计数器
- 创建工作线程。这里并不是直接new Thread,而是通过包装的Worker创建
- 如果创建成功,将线程放入工作线程队列,并且启动
- 如果启动失败,则进行清理:将新线程中断,线程计数器递减,并将线程从工作线程移除
其中上面的线程启动失败的方法有3步
private void addWorkerFailed(ThreadPoolExecutor.Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 将任务从工作队列移除
if (w != null)
workers.remove(w);
// 工作线程计数器递减
decrementWorkerCount();
// 中断线程
tryTerminate();
} finally {
mainLock.unlock();
}
}
这里有一点要说明的是,java中并没有提供直接停止线程的方法,一般都是采用中断的方式,这个在后面会分析到
线程池的状态
上面在创建线程的过程中,会判断线程池的状态,那么线程池有哪几个状态呢?
// 接受并执行新任务
private static final int RUNNING = -1 << COUNT_BITS;
// 不接受新任务,但会执行任务队列中已存在的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 不接受新任务,不会执行任务队列中已存在的任务,并且会中断正在执行的任务
private static final int STOP = 1 << COUNT_BITS;
// 工作线程为0,并且任务都是terminated状态,到TERMINATED之前的过渡状态
private static final int TIDYING = 2 << COUNT_BITS;
// 线束方法执行完成
private static final int TERMINATED = 3 << COUNT_BITS;
其中的状态转换如下面的状态机所示
- 如果executor执行shutdown(),状态会由RUNNING变成SHUTDOWN
- 如果是执行shutdownNow(),状态会直接变成STOP
- 如果线程池pool变为空,则会状态转换成TIDYING
- 如果在TIDYING状态,对应的钩子方法terminated()执行完成,最终会变成TERMINATED
看下shutdown()方法,里面会将状态设置为SHUTDOWN:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 设置为SHUTDOWN状态
advanceRunState(SHUTDOWN);
// 中断空闲的线程
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
再看下shutdownNow(),里面会将状态设置为STOP:
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 设置状态为STOP
advanceRunState(STOP);
// 中断线程
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
这2个方法的关键就在于interruptIdleWorkers与interruptWorkers的区别:shutdown()是中断未执行任务的线程
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
// 如果是没有中断的,则尝试中断
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
而shutdownNow是将正在执行中的任务也进行中断:根据当前线程的状态来判定,如果是state>0,则表示正在执行,将进行中断
void interruptIfStarted() {
Thread t;
// 线程状态 >=0,并且未被中断
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
看一个具体的例子:如果线程池执行了shutdown()或者shutdownNow()方法,这时线程池的状态不再RUNNING,会转换到SHUTDOWN或者STOP,此时再提交任务,则会执行相应的拒绝策略
public void cachedThreadPoolTest() {
// 可缓存的线程池
ExecutorService executor = Executors.newCachedThreadPool();
executor.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello world");
}
});
// 关闭线程池,不再接受任务
executor.shutdown();
// executor.shutdownNow();
// 再次提交任务
executor.submit(() -> {
System.out.println("hello world");
});
}
运行test程序,会抛出异常
看下submit()方法,除了返回Future之外,真正调用的也是execute()方法,这时候发现线程池状态变化,addWorker肯定返回false,进而执行拒绝策略
而默认的拒绝策略就是抛出异常,所以上面的程序会抛出异常
线程回收
前面介绍线程池核心参数时,有一个线程存活时间keepAliveTime,再看下它的作用
keepAliveTime:线程存活时间。在上面的情况中,如果创建的线程数量超过了核心线程数,并且在keepAliveTime时间段内,没有新的任务执行,就回把多余的线程进行回收。
那么线程池具体是怎样回收线程呢?
前面讲了,线程池是通过Worker-工作线程来创建的,然后启动线程
看下Worker类图,实现Runnable接口,则会覆写run()方法,线程启动,用于执行任务,里面又调用了runWorker
看下真正的runWorker()
final void runWorker(ThreadPoolExecutor.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) {
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);
}
}
从上面看,主要是getTask()和processWorkerExit(w, completedAbruptly)决定是否回收的
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.
// 如果线程池关闭,则返回null,将执行线程退出
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
// 是否超时由allowCoreThreadTimeOut标识true,或者超过核心线程
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 如果超时,则返回null,将执行线程退出
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 如果允许超时,则执行带超时的方法从阻塞队列获取任务,反之,则由不超时的take()方法获取任务
// take()不会超时,会一直阻塞队列直到取到任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
从上面可以看出,超时有2种情况
- 是线程池关闭,将会当作超时对待,回收线程
- 设置允许回收allowCoreThreadTimeOut为true,且从任务队列获取任务超时(这种主要是超时时间段没有可执行任务),也将进行回收(对core-max部分)
AQS
上面代码里面一起出现ReentrantLock及getState(),里面涉及到AQS,将在下一节中介绍