ThreadPoolExecutor
优点:在某些应用场合下,我们会产生大量的线程,想必大家都遇到过。当时用的时候没怎么在意。看了线程池之后就觉得直接产生大量的线程,这种做法是不明智的。
在产生大量线程的情况下我推荐使用线程池,因为线程池每次只有规定数量的线程在运行。这样更便于管理。下面就来了解线程池的工作原理
线程池的组成
了解线程池较为重要的成员。
// AtomicInteger可以得到线程池现在的状态和现在工作的工人数量。高位存放状态,地位存放个数。
// 初始化时ctl得到的值为RUNNING
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 这个数值结果是29,一共32位,以29为分割线,高三位存放状态,低29位存放工人数量。
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 这些是线程池的状态
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
private static int ctlOf(int rs, int wc) { return rs | wc; }
通过下面这个我们可以发现一个有趣的现象,ctl得到的值与CAPACITY相与就可以得到低29的值,也就是工人的数量。
要是ctl得到的值与CAPACITY的非相与,就可以得到高3位的值,也就是状态。
private final BlockingQueue<Runnable> workQueue;
// 存放任务的队列,一个线程就是一个任务。任务队列。
private final HashSet<Worker> workers = new HashSet<Worker>();
// 工人存在的地方,产生一个工人,就放在这里,工人之家。
private int largestPoolSize;
// workers的大小,也就是存在的工人的数量。
private long completedTaskCount; // 已完成任务的计数器
private volatile long keepAliveTime;
// 线程等待的时间(后面会说明这个时间)
private volatile int corePoolSize;
// 规定核心工人数量
private volatile int maximumPoolSize;
// 规定最大工人数量
当我们产生一个ThreadPoolExecutor对象,此时没有工人。只有在调用execute(Runnable command)方法的时候才有可能会产生工人。
worker类
我只截取了worker类的一部分。我们可以看到worker类实现了Runnable接口所以Worker类本身就是一个线程,每个工人都有任务成员。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
final Thread thread;
//通过thread来启动worker这个线程
Runnable firstTask;
// firstTask是工人要完成的任务
volatile long completedTasks;
// 记录完成的任务数量。
Worker(Runnable firstTask) {
setState(-1);
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
}
线程池工作原理
调用这个方法将任务加到线程池里。这有分为3种情况
- 第一种:此时工人的数量小于核心工人数量,
- 于是在线程池还在运行的状态下产生一个工人,
- 并将其加入到工人之家workers,
- 并且工人的数量加一。
- 启动工人的线程
- 工人开始执行任务
- 第二种: 此时工人的数量已经大于核心工人的数量,此时我们不在产生新的工人,而是将任务放在上面说过的任务队列里 。工人执行处理完自己手上的任务之后,就会去队列里取任务执行。这里工人的处理仅仅只是启动任务这个线程而已。
这里有两个问题,
- 这里用到的队列是我们自己设置进来的,如果是ArrayBlockingQueue,那不比担心,因为这个的初始化要设置队列的大小。而如果我们用的是LinkedBlockingQueue,那么我们在初始化的时候我建议要设置队列的的大小。因为我们将任务放到队列里如果不设置大小,那么他的默认大小为231-1。这个数字相当大了。
- 还有一个问题,若是任务队列没有任务了怎么办,工人去队列里取任务没有任务了怎么办?这个说完下一个情况再来解释。
- 第三种:假设线程真的很多终于连任务队列也放完了,那么我们就不能将任务放到队列里面去了。这个时候我们就又要产生新的工人了,去帮别的工人分担这些任务。当然这个工人也不是无休止的一直增加。我们之前设置了最大工人数量,那么产生的工人不能超过这个数量。如果超过了这个数量我们就要执行拒绝再加入任务。
这是线程池工作的一个大概思路。就之前的那个问题
我们的工人在执行完自己任务之后就会去队列里看一眼有没有任务需要我执行的,有的话,拿来执行就好,但是要是没有那么就分为两种情况了。
我们要看这个时候工人的数量是否超过了核心工人的数量,
- 没有超过,那么这个工人就被阻塞起来,直到有新的任务加到队列里,唤醒被阻塞的工人
- 超过了,那么会让这个工人等一段时间,这个时间就是我们之前设置的keepAliveTime。时间到了还没有任务,那么就会将这个工人杀掉。从工人之家删除掉,并且工人数量减一。
源码
我这里只展示了几个重要的方法
execute方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// c的值 4个字节,高三位表示线程池的状态,低29位表示工人的数量
if (workerCountOf(c) < corePoolSize) {
// workerCountOf方法可以得到工人的数量,
if (addWorker(command, true))
// 这里的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))
// 此时就连队列里也满了,那么就产生新的工人,false表示工人数量大于核心
// 工人数量。如果加入失败,就拒绝任务再加入。
reject(command);
}
补充:线程池里有三个内部类,AbortPolicy、DiscardPolicy、DiscardOldestPolicy。这三个内部类都实现了RejectedExecutionHandler接口,用来实现拒绝策略。在线程池中RejectedExecutionHandler用的是AbortPolicy类。所以我们用的拒绝策略正是下面中的方法。向外界抛异常。
// 这是拒绝任务加入的代码,
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
addWorker方法
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// rs是线程池的状态
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;
}
}
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 {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive())
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;
}
runWorker方法
这个方法就是worker线程启动后执行的方法。
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) {
// 当前任务执行完之后,task被赋值为null,第二次循环就会从队列里取
// 任务,任务取到就继续。若队列里没有任务,那么就阻塞这个worker线
// 程,这个工人就暂时休息吧,等有任务了再来工作。
w.lock();
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() {
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;
}
}
}