java提供的调度框架如上图所示,最顶层接口Executor定义了执行任务的接口execute,其下有两个关键类实现了执行、调度的方法。
java线程池提供了一个工厂类Executors来创建线程池,主要提供了以下几种:FixedThreadPool、SingleThreadExecutor、CachedThreadPool,以及可定时运行的线程池。本文重点介绍前三种池的原理以及ThreadPoolExecutor的实现。
首先介绍一下基础概念:工作线程、任务队列。如果任务到来,线程池创建一个线程来处理任务,会将该任务包装成一个Worker(实现了Runable接口),这个Worker要么被销毁,要么一直处理任务或者等待处理任务,是干活的,所以称之为worker,实质上是一个线程。任务队列是线程池维护的一个阻塞队列,如果没有线程可以处理任务,则任务进入任务队列。
public static ExecutorService newFixedThreadPool(intnThreads) {
return new ThreadPoolExecutor(nThreads,nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
通过ThreadPoolExecutor创建了固定大小的池,任务队列采用的是无界的阻塞队列,如果任务到来,活动线程数目小于nThreads,无论当天线程是否idle,则都会创建新的线程执行任务。如果线程数目大于nThreads则会丢入到队列里面。那么第一种情况下,为什么总是创建新的线程来处理?是因为如果线程无任务处理,则会去队列拿任务,如果队列为空,则会被阻塞,所以,这里的空闲,只是只无任务可以执行,实际上线程是被阻塞了,等到队列中有任务方被唤醒。该种池,第三个参数是无效的,即空闲线程不会被超时销毁,具体后面介绍ThreadPoolExecutor时讨论。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
该模式创建了一个大小为1的线程池,任务队列是无界的。其保证了任务的顺序执行。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
该模式创建了一个相对弹性的池,基础线程数目为0, SynchronousQueue队列(不存任务)。如果任务到来,空闲线程会处理到来的任务,如果没有可用线程,则创建新的线程,直至Integer.MAX_VALUE,当然,60s后如果线程依然空闲则会销毁。注意了,这个和固定池不一样的一个地方是,这里第三个参数会生效, 具体后面介绍 ThreadPoolExecutor时讨论。
ThreadPoolExecutor的实现:
public ThreadPoolExecutor(intcorePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize,maximumPoolSize, keepAliveTime,unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
第一个参数,是一个基础线程数目限制,第二个是最大线程数目限制,第三个是空闲线程(取不到任务的线程)的存活时间,最后一个是用来做任务保存的阻塞队列。ThreadPoolExecutor工作原理是:如果线程到来,当天池中线程数目小于corePoolSize,则会创建新的线程,原因上面讲过。如果大于corePoolSize,则任务进入任务队列,如果队列满了,则继续创建新的线程直至maximumPoolSize限制。该类核心方法:
public void execute(Runnablecommand) {
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();
if (workerCountOf(c) <corePoolSize) {//当天工作线程小于核心池大小,则创建core worker并入worker队列
if (addWorker(command,true))
return;
c = ctl.get();
}
if (isRunning(c) &&workQueue.offer(command)) {//往任务队列中添加任务,注意此处不阻塞,可能会失败,比如newCachedThreadPool
int recheck =ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);//1
}
else if (!addWorker(command,false))//2 总之1和2处会添加一个工作线程,该线程被标记为非core的
reject(command);
}
下面看addWorker实现
private boolean addWorker(RunnablefirstTask, booleancore) {
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))//通过cas设置工作线程数目
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 {
final ReentrantLock mainLock = this.mainLock;
w = new Worker(firstTask);//生成一个worker线程,firstTask第一个工作任务,会立即调度
final Thread t = w.thread;
if (t !=null) {
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int c = ctl.get();
int rs = runStateOf(c);
if (rs <SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())// precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);//添加worker
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);
}
returnworkerStarted;
}
woker任务调度:
final void runWorker(Workerw) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask =null;
w.unlock(); // allow interrupts
booleancompletedAbruptly = true;
try {
while (task !=null || (task = getTask()) !=null) {//获取task,是一个循环,如果跳出循环,则线程可能会被销毁或者被一个新的线程取代,见finally中方法
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方法:
private Runnable getTask() {
booleantimedOut = false; // Did the last poll() time out?
retry:
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;
}
booleantimed; // Are workers subject to culling?
for (;;) {
int wc = workerCountOf(c);
timed =allowCoreThreadTimeOut || wc >corePoolSize;//newCachedThreadPool中corePoolSize为0所以timed是true
if (wc <=maximumPoolSize && ! (timedOut &&timed))
break;
if (compareAndDecrementWorkerCount(c))//通过cas减少工作线程数目
return null;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) !=rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) ://此处关键了:如果timed为true,则poll会在超时后返回null,这就是为什么newCachedThreadPool中第三个参数会有效的原因
workQueue.take();//如果timed为false,则会一直阻塞,这就是为什么固定大小的池第三个参数无效的原因
if (r !=null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}