ThreadPoolExecutor构造函数
首先看下线程池构造函数中的几个配置项含义
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize
:核心线程数
maximumPoolSize
:最大线程数
keepAliveTime
:总线程数大于核心线程数时,空闲线程允许存活的时间(这里并不严谨,后面会讲)
unit
:keepAliveTime
的单位
workQueue
:任务队列
threadFactory
:用来创建线程的工厂
handler
:拒绝策略
execute方法
execute()
是线程池执行任务的入口,submit()
实际上也是调用了execute()
public void execute(Runnable command) {
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);
}
其中ctl
用于存储当前线程池状态和线程总量,addWorker(Runnable firstTask, boolean core)
用于创建线程
execute()
应对三种情况:
-
工作线程 <
corePoolSize
新建一个核心线程来执行任务
-
工作线程 >=
corePoolSize
且workQueue
没满将任务添加到
workQueue
,如果当前池中没有线程就新建一个 -
工作线程 >=
corePoolSize
且workQueue
已满尝试创建线程来执行任务
中间出问题了会采取拒绝策略reject(command)
看完这个方法就能知道系统提供的几种线程池的特点了
-
FixedThreadPooler
new LinkedBlockingQueue<Runnable>()
创建出的是一个容量为Integer.MAX_VALUE
的队列public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
特点是
workQueue
的大小被设置成了Integer.MAX_VALUE
,根据上面的三种情况,当线程数达到corePoolSize
时,所有的任务都会被插入到workQueue
中,最多同时执行corePoolSize
个任务。这样maximumPoolSize
也就没有意义了,所以被设置成了与corePoolSize
一样的值。 -
SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
其实就是
Executors.newFixedThreadPool(1)
,特点跟上面那个一样,只是corePoolSize
被设置成了1,每次只能有一个任务在执行 -
CachedThreadPool
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
特点是
corePoolSize
为0,maximumPoolSize
为Integer.MAX_VALUE
,SynchronousQueue
是个没有容量的队列,如果当前没有空闲线程正在poll
或者take
,offer
就会返回false
,所以任务进来时,只要当前没有空闲线程,CachedThreadPool
就会新建一个线程来执行任务。也就是说,所有任务都会立刻开始执行。
addWorker方法
private boolean addWorker(Runnable firstTask, boolean core) {
boolean workerStarted = false;
Worker w = null;
w = new Worker(firstTask);
final Thread t = w.thread;
workers.add(w);
t.start();
workerStarted = true;
return workerStarted;
}
addWorker
方法比较长,但是去掉各种状态检测,实际上就干了两件事
- 创建
Worker
并添加至workers
- 启动
Worker
中的Thread
关于它是如何执行任务的,还得看一下Worker
这个类(Worker
是ThreadPoolExecutor
的内部类)
private final class Worker extends AbstractQueuedSynchronizer implements Runnable
{
final Thread thread;
Runnable firstTask;
Worker(Runnable firstTask) {
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
}
Worker
实现了Runnable
接口,创建线程时把自己作为参数传了进去,run()
方法中调用了ThreadPoolExecutor
的runWorker
方法
final void runWorker(Worker w) {
Runnable task = w.firstTask;
w.firstTask = null;
while (task != null || (task = getTask()) != null) {
task.run();
task = null;
}
}
简化一下就是这样子的,如果创建Worker
传入的firstTask
不为null
,那么首先会执行firstTask
,执行完毕后,该线程会尝试通过getTask
方法继续获取任务来执行,当getTask
返回null
时,线程就会运行结束
getTask方法
private Runnable getTask() {
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
if ((rs >= SHUTDOWN && workQueue.isEmpty()) || rs >= STOP ) {
return null;
}
int wc = workerCountOf(c);
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;
}
}
}
先不管别的,直接看从workQueue
中获取任务的代码
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
根据timed
字段来决定是使用poll
方法还是take
方法,take
方法会一直阻塞直到获取到任务,而poll
如果不能在规定时间内获取到任务就会超时返回null
,timeOut
会被标记为ture
,下次循环时会返回null
让正在通过getTask
方法获取任务的线程结束
timed
是由allowCoreThreadTimeOut
和工作线程数决定的,当allowCoreThreadTimeOut
为true
或者工作线程数大于corePoolSize
时,timed
就会被标记为ture
,前者表示允许核心线程超时,后者表示当前工作线程大于核心线程数,所以当allowCoreThreadTimeOut
为fales
时,线程池的大小达到或超过corePoolSize
后,线程池总会留着corePoolSize
个线程不会去关闭(工作线程小于等于corePoolSize
时,timed
为false
,会通过take
方法从workQueue
获取任务,所以不会超时关闭)
再看下其他代码,会让线程停止的几种情况:
- 线程池已经
SHUTDOWN
且workQueue
为空 - 线程池已经
STOP
- 工作线程数大于
maximumPoolSize
- 线程获取任务超时
总结
-
构造函数中的三个重要的配置项
corePoolSize
:如果allowCoreThreadTimeOut
为false
,当线程池的大小达到或超过corePoolSize
后,线程池总会留着corePoolSize
个线程不会去关闭maximumPoolSize
:允许存活的最大线程数,超过的线程会被关闭keepAliveTime
:如果allowCoreThreadTimeOut
为false
,总线程数大于核心线程数时,空闲线程允许存活的时间。如果allowCoreThreadTimeOut
为true
,不管当前有多少线程,空闲线程允许存活的时间。 -
如果
workQueue
没满,线程池最多只会让corePoolSize
个线程同时工作,不会新建线程 -
线程运行完一个任务后会通过
getTask
方法继续获取任务,从而达到线程复用的目的