前言
在刚接触多线程编程时,曾写过类似于如下代码:
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 20, 1, TimeUnit.HOURS, new LinkedBlockingDeque<>());
Thread thread = new Thread(() -> System.out.println("子线程运行"));
poolExecutor.submit(thread);
thread.join();
}
既想要使用线程池来管理线程,又想要阻塞当前线程。结果运行时发现thread.join()
方法并没有起作用,究其原因,thread
线程并没有启动,然而确实有子线程运行并打印了数据,因此有必要了解一下submit()
方法的执行原理。
ThreadPoolExecutor
从上图可以发现 ThreadPoolExecutor
类相关的继承关系,而submit(Runnable)
方法在其父类AbstractExecutorService
中有具体实现:
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
从这里就能发现原有线程不启动的原因了,submit()
方法接收一个Runnable
参数对象,封装成FutureTask
,并执行该任务,而不是启动原来的线程。
线程池处理任务的流程
ctl
线程池内部维护一个AtomicInteger
类型的变量ctl
,通过位运算的方式以该数字表示线程池的两个状态,原理为:二进制高 3 位表示 runState,低 29 位表示workerCount。
-
workerCount:存活的线程数
-
runState:线程池当前的运行状态
RUNNING
: 接受新任务,处理等待队列中的任务。SHUTDOWN
: 不接受新任务,处理等待队列中的任务。STOP
: 不接受新任务,丢弃等待队列中的任务,中断正在运行的任务。TIDYING
: 当所有的任务已终止,workCount 为0,线程池会变为 TIDYING 状态。接着会执行terminated()
函数。TERMINATED
:terminated()
函数执行完成。
execute()
线程池处理任务的主要方法为execute()
方法,下面看一下它的源码:
public void execute(Runnable command) {
if (command == null) throw new NullPointerException();
int c = ctl.get();
//1. 如果当前工作线程数小于 corePoolSize
if (workerCountOf(c) < corePoolSize) {
//创建核心线程,并将 command 作为其绑定运行的第一个任务
if (addWorker(command, true))
return;
c = ctl