之前写了线程池参数的作用:https://blog.csdn.net/u010900754/article/details/80993645
这次看下实现。
首先,线程池的作用,在ThreadPoolExecutor类的注释里已经写得很清楚了:
* <p>Thread pools address two different problems: they usually
* provide improved performance when executing large numbers of
* asynchronous tasks, due to reduced per-task invocation overhead,
* and they provide a means of bounding and managing the resources,
* including threads, consumed when executing a collection of tasks.
* Each {@code ThreadPoolExecutor} also maintains some basic
* statistics, such as the number of completed tasks.
1.避免了每一次执行都去创建销毁线程的开销;
2.资源可控;
下面看下实现:
成员变量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private final BlockingQueue<Runnable> workQueue;
private final HashSet<Worker> workers = new HashSet<Worker>();
private volatile long keepAliveTime;
private volatile int corePoolSize;
private volatile int maximumPoolSize;
最核心的几个变量。
ctl:线程池的线程数目以及状态。这里是用了一个Integer的两段bit来标识数目和状态的,取值通过预设的掩码来做位运算;
workQueue:任务等待队列,如果工作线程数目大于coreSize,新来的任务进入队列;
workers:所有的工作线程;
keepAliveTime:临时线程的存活时间;
corePoolSize:核心线程数目,不会销毁;
maximumPoolSize:最大工作线程数目,超过以后任务直接拒绝;
线程池的构建一般都是通过Executors的静态工厂方法来构建的,不同的参数代表不同的线程池配置,但是最终都是调用的TheadPoolExecutor的几个重载构造函数。
任务的执行,一般是通过ExecutorService接口的submit方法提交的:
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
TheadPoolExecutor里面实现了execute方法:
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();
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);
}
这个方法很重要,之前介绍的线程池参数就是在这里发挥作用的。
1.首先看当前线程数是否小于corePoolSize,如果是,直接addWorker,且work是一个常驻work,需要传入core=true参数;
2.否则就尝试将任务扔进等待队列,之前看到,这个队列是一个blockingQueue,可以在构造的时候指定大小,所以,如果满了,这里调用offer方法就会失败,如果失败就会进入下面的步骤;
3.尝试新增临时线程。这里传入的core=false的参数,如果已经达到了mamximumPoolSize,那么新增临时线程失败,就会reject任务;
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;
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;
}
主要就是wc >= (core ? corePoolSize : maximumPoolSize))这一步判断,是否可以新增worker。
最后新增完会主动调用worker的thread的start方法来启动线程。
这里有一个注意点,提交任务时都是以当前worker线程数目而不是任务数目为依据的。
原因看了worker工作线程的实现就知道了。大致就是,工作线程并不是与任务一一对应的,一个工作线程执行完了会从queue里面取新的线程。
下面是worker实现:
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
Worker继承了Runable接口,是一个ThreadPoolExecutor的内部类;
成员变量:
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
主要就这两个,一个线程一个初始任务。
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
线程是在构造函数里new出的一个线程。
public void run() {
runWorker(this);
}
run方法很简单,调用的是外部类的runWorker方法:
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) {
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);
}
}
work会不断地从queue里面拿任务,然后执行。那么如果任务为空呢?即while循环里返回了false,就会走到finally块的processWorkerExit方法来回收worker。
先看下while循环里的getTask方法:
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;
}
}
}
该方法其实承载了回收超时临时线程的一部分职责。
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
这个timed参数表示此次调用是否可以回收worker,一般情况下不会设置allowCoreThreadTimeOut(让coreThead也被回收),那么就是看wc > corePoolSize,也即当前线程数是否大于coreSize。
然后根据timed参数取任务:
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
如果timed==true,那么就是一个带超时的poll,如果在keepAliveTime内仍未取到任务,就会返回null,然后上一层的runWorker的while循环就会退出,导致worker被回收。
否则如果times==false,那么就是一个阻塞的take,会一直取直到有任务返回。所以上一层的runWorker的while循环不会退出,也就是说线程数目小于coreSize时,线程不会被销毁。
这里注意两点:
1.线程没有先后关系,不是说早进来的线程就一定是core的;
2.线程池的addWorker一系列判定是以线程数目而非任务数目;
总结,看了线程池的源代码,才发现设计思路是如此的简单坚固。只是用了一个队列解耦了应用层和执行层的线程。应用层只需要往队列里面扔任务,worker线程只需要从队列里面取任务,如果数目超了coreSize,取不到任务的临时线程就会被剔除。不再需要其他角色参与,比方说任务分配,额外维护临时线程。