前言
使用线程池能够提高线程的复用率,避免不必要的创建线程,能够节约内存空间和CPU运行时间。除此之外用线程池作为接口执行任务能够将任务的提交与执行解耦,在线程池内部决定任务的分配,具有良好的内聚性。
常用线程池
java.util.concurrent.Executors是一个线程池工厂,负责创建常用的线程池,主要有如下几种:
FixedThreadPool 一个固定线程数量的线程池:
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
CachedThreadPool 不固定线程数量,且支持最大为Integer.MAX_VALUE的线程数量:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
SingleThreadExecutor 可以理解为线程数量为1的FixedThreadPool:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
ScheduledThreadPool 支持定时以指定周期循环执行任务:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
其中前三种线程池是ThreadPoolExecutor不同配置的实例,最后一种是ScheduledThreadPoolExecutor的实例。
ThreadPoolExecutor
先来通过ThreadPoolExecutor的构造方法了解一下这个类:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
主要参数有:
corePoolSize 核心线程的数量,不超过这个参数数量的线程会被保留在线程池内,即使它们是空闲的,如果设置了allowCoreThreadTimeOut为true除外。
maximumPoolSize 线程池所允许拥有线程的最大数量,当任务队列的任务已满,线程数已达到最大数量,任务会被拒绝。
keepAliveTime 当线程池的线程数量超过核心线程的数量,这些非核心线程会尝试在keepAliveTime内获取队列内的任务,如果获取失败则被线程池移除并终止。
unit 超时时间的单位。
workQueue 任务的阻塞队列,缓存将要执行的Runnable任务,由各线程轮询该任务队列获取任务执行。
threadFactory 线程创建的工厂。
handler 当任务由于线程数量或者任务队列达到上限,会执行该接口的方法处理任务的拒绝。
ThreadPoolExecutor的状态变量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
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;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
其中ctl是ThreadPoolExecutor的同步状态变量。
workerCountOf()方法取得当前线程池的线程数量,算法是将ctl的值取低29位。
runStateOf()方法取得线程池的状态,算法是将ctl的值取高3位:
- RUNNING 111 表示正在运行
- SHUTDOWN 000 表示拒绝接收新的任务
- STOP 001 表示拒绝接收新的任务并且不再处理任务队列中剩余的任务,并且中断正在执行的任务。
- TIDYING 010 表示所有线程已停止,准备执行terminated()方法。
- TERMINATED 011 表示已执行完terminated()方法。
Executor.execute(Runnable command)
该方法将使用线程池执行Runnable对象的run()方法:
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) {
//情况1
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
//情况2
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false)) //情况3
reject(command);
}
以上代码对应了三种情况:
- 线程池的线程数量小于核心线程数量上限,开启核心线程执行任务。
- 线程池的线程数量不小于核心线程数量上限,或者开启核心线程失败,尝试将任务以非阻塞的方式添加到任务队列。
- 任务队列以满导致添加任务失败,开启新的非核心线程执行任务。
回顾FixedThreadPool,因为它配置的corePoolSize与maximumPoolSize相等,所以不会执行到情况3,并且因为workQueue为默认的LinkedBlockingQueue,其长度为Integer.MAX_VALUE,几乎不可能出现任务无法被添加到workQueue的情况,所以FixedThreadPool的所有任务执行在核心线程中。
而CachedThreadPool的corePoolSize为0,表示它不会执行到情况1,因为它的maximumPoolSize为Integer.MAX_VALUE,所以几乎没有线程数量上限,因为它的workQueue为SynchronousQueue,所以当线程池里没有闲置的线程SynchronousQueue就会添加任务失败,因此会执行到情况3添加新的线程执行任务。
addWorker方法
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
//第一个循环轮询线程池的状态,如果线程池处于SHUTDOWN及大于它的状态则拒绝执行任务
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;
//第二个循环就尝试将当前线程数+1
//如果是核心线程当前线程数必须小于corePoolSize
//如果是非核心线程则当前线程数必须小于maximumPoolSize
//如果当前线程数小于线程池支持的最大线程数CAPACITY 也会返回失败
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
}
}
//这里已经成功执行了CAS操作将线程池数量+1,下面创建线程
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask); //firstTask的优先级高于任务队列
final Thread t = w.thread; //Worker内部有一个Thread,并且执行Worker的run方法,因为Worker实现了Runnable
if (t != null) {
//这里必须同步在状态为运行的情况下将Worker添加到workers中
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;
}
addWorker这个方法先尝试在线程池运行状态为RUNNING并且线程数量未达上限的情况下通过CAS操作将线程池数量+1,接着在ReentrantLock同步锁的同步保证下判断线程池为运行状态,然后把Worker添加到HashSet workers中。如果添加成功则执行Worker的内部线程。
Worker构造方法
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
这里指定了第一个要执行的任务,并通过线程池的线程工厂创建线程。可以发现这个线程的参数为this,即Worker对象,因为Worker实现了Runnable因此可以被当成任务执行,执行的即Worker实现的run方法:
public void run() {
runWorker(this);
}
runWorker方法
因为Worker为ThreadPoolExecutor的内部类,因此runWorker方法实际是ThreadPoolExecutor定义的:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 因为Worker的构造函数中setState(-1)禁止了中断,这里的unclock用于恢复中断
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);
}
}
这个方法是线程池复用线程的核心代码,注意Worker继承了AbstractQueuedSynchronizer,在执行每个任务前通过lock方法加锁,执行完后通过unlock方法解锁,这种机制用来防止运行中的任务被中断。在执行任务时先尝试获取firstTask,即构造方法传入的Runnable对象,然后尝试从getTask方法中获取任务队列中的任务。在任务执行前还要再次判断线程池是否已经处于STOP状态或者线程被中断。
注意这里w.lock方法是在获取到任务后才执行的,也就是如果线程获取到任务前都未加锁,这样能保证showDown方法尝试获取该锁中断空闲的线程,详见后面的解析。
当线程被中断、抛出异常、不能及时得到任务,processWorkerExit方法用于最后将线程回收。
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;
}
}
}
还记得Executor.execute方法的情况2是将任务添加到任务队列,getTask方法就是从任务队列中同步地取出任务。
这个方法通过一个循环不断轮询任务队列有没有任务到来,首先判断线程池是否处于正常运行状态,通过超时配置有两种方法取出任务:
- BlockingQueue.poll 阻塞指定的时间尝试获取任务,如果超过指定的时间还未获取到任务就返回null。
- BlockingQueue.take 这种方法会在取到任务前一直阻塞。
FixedThreadPool使用的是take方法,所以会线程会一直阻塞等待任务。CachedThreadPool使用的是poll方法,也就是说CachedThreadPool中的线程如果在60秒内未获取到队列中的任务就会被终止。
到此ThreadPoolExecutor是怎么执行Runnable任务的分析结束。
ExecutorService.shutdown()
既然有任务的执行,就少不了任务的终止。ExecutorService是Executor的子类,也是ThreadPoolExecutor的基类。
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
首先通过mainLock加锁同步修改线程池状态为SHUTDOWN,然后通过interruptIdleWorkers方法中断空闲线程,OnShowDown方法是留给子类去实现的。
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
//未中断的,且tryLock成功
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne) //只做了一次循环就结束了,因此最多只能中断一个线程
break;
}
} finally {
mainLock.unlock();
}
}
interruptIdleWorkers(boolean onlyOne)方法也是先用mainLock加锁同步,然后循环找出所有Worker中Thread未中断的,通过tryLock方法尝试获取锁。还记得上文的runWorker方法Worker的锁是在获取任务时才加的,interruptIdleWorkers方法通过竞争该锁抢先中断线程,这样就导致未执行任务的线程被中断了,而正在执行任务的线程不受影响,并且可以继续执行任务队列中的任务。
ExecutorService.shutdownNow()
与ExecutorService.shutdown()不同的是,shutdownNow方法除了让线程池拒绝接收新的任务,并且不再执行任务队列里未执行的任务。
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
首先mainLock同步将状态改为STOP,然后中断所有线程。
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
interruptWorkers方法将对所有Worker执行interruptIfStarted,即将所有运行中的线程中断:
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
还记得Worker的构造函数中执行了setState(-1),而在runWorker方法中通过unlock将state改为0,因此可以被interruptWorkers方法中断。
这里注意的是中断并不意味着线程就一定停止工作,除非在任务中捕获InterruptedException退出任务。
ExecutorService.submit(Callable<T> task)
使用该方法可以执行一个带返回值的Callable任务,通过该对象的call()方法定义线程要执行的事情,同时call()方法的返回值也由开发者定义,该返回值可以通过ExecutorService.submit返回的Future对象的get方法阻塞获取。
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
submit方法的执行过程中,实际是通过newTaskFor方法把Callable对象转换为RunnableFuture对象,再由上述的execute(Runnable task)方法执行的。
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
newTaskFor方法实际是构造了一个FutureTask对象返回,RunnableFuture是FutureTask的超类,并且实现了Runnable和Future接口。所以execute方法必然会执行FutureTask的run方法。
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
public class FutureTask<V> implements RunnableFuture<V> {
...
}
FutureTask工作原理
首先来看下它的内部状态:
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
当新建一个FutureTask,其初始状态为NEW。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
FutureTask任务的执行
下面分析FutureTask的run方法:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
在FutureTask的run方法中,可以看到实际上调用了内部Callable对象的call方法得到执行结果。而当call方法执行结束,如果没有异常就执行set(result)方法,有异常则执行setException方法。
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
两个方法都通过UnSafe类的CAS方法将stateOffSet分别修改为NORMAL或EXCEPTIONAL,然后调用finishCompletion方法:
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
该方法遍历了一个waiters链表,取出WaitNode中封装的线程,调用LockSupport.unpark唤醒线程。补充一下LockSupport是JDK中一个底层同步类,内部通过UnSafe类实现park与unpark方法用来阻塞或者唤醒线程。
FutureTask得到任务的结果
剩下一个疑问,FutureTask调用get方法是如何阻塞等待结果的?
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
如果在任务成功执行并返回后调用get,因为状态已经更新为NORMAL大于COMPLETING,直接返回report(s),否则执行awaitDone方法。
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
这段代码需要配合finishCompletion方法理解:
1、当任务已完成,则返回状态
2、当任务将要完成通过Thread.yield()将当前线程状态从运行状态变为就绪状态,从而提高其他线程竞争运行的可能性,将当前状态改为NORMAL。
3、如果任务未完成就创建一个WaitNode对象,内部持有一个当前线程的引用,并且添加到waiters链表上,最后通过LockSupport.park阻塞线程。
因此任务完成后finishCompletion方法就作用就是将waiters链表的每个WaitNode中的线程唤醒,以便执行report(s)。
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
report方法将会返回给调用FutureTask.get方法的线程Callable任务的结果或者异常。
FutureTask任务的取消
ExecutorService.submit方法会返回一个Future对象,上文已经分析过,它实际是FutureTask对象向上转型。
因此Future的cancel方法是在FutureTask中实现的。
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
首先通过CAS操作尝试将FutureTask状态从NEW改为INTERRUPTING 或CANCELLED。如果FutureTask的状态不为NEW,也就是任务已经执行完,cancel方法会被视为无效返回。因此cancel方法只适用于未开始执行或者已经开始执行但未完成的任务。
参数mayInterruptIfRunning如果为true,则将状态设置为INTERRUPTING,然后尝试将运行任务的线程runner对象中断。回顾run方法中的UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread())就是利用CAS操作将runner对象赋值为任务的执行线程。最后将FutureTask的状态同步设置为INTERRUPTED。
如果mayInterruptIfRunning为false则不会尝试中断线程,而是将FutureTask的状态同步设置为CANCELLED。
最终执行finishCompletion方法,这个方法上文已经分析过,它会将所有调用get方法阻塞等待结果的线程唤醒,并调用done方法。所以一旦调用cancel,已经调用get方法的线程将被立即唤醒并返回CancellationException异常。
如果任务在队列里未被执行,cancel方法已经将FutureTask的状态改为INTERRUPTING或者CANCELLED,
当线程获取到这个FutureTask执行它的run方法时,判断它的状态已经不是NEW,所以会直接返回,因此对于未执行的任务执行了cancel方法将不会被执行。
对于已经开始执行但未执行结束的线程,可以通过设置mayInterruptIfRunning为true尝试将线程中断从而捕获异常退出。如果任务中没有处理InterruptedException则会将任务执行完,但是在执行set方法返回结果时,CAS操作判断状态已经不是NEW因此不会执行后续操作。
ThreadPoolExecutor小结
分析了ThreadPoolExecutor执行execute、submit、shutdown、shutdownNow等方法的执行过程,并且衍生分析了FutureTask的工作原理。发现其中大量用到了以UnSafe类为基础的同步锁,后续会继续探究ReenTrantLock、AbstractQueuedSynchronizer、LockSupport等同步类的原理。
限于篇幅ScheduledThreadPool 留在以后分析。
最后总结一下3个以ThreadPoolExecutor为实现类的线程池:
- SingleThreadPool 适合维护固定稳定的单线程任务。
- FixedThreadPool 适合维护固定且稳定的多个线程,而不用担心任务数量过多导致的同时创建多个线程的问题。它的缺陷是要注意任务的阻塞问题,一旦线程池内的所有线程都阻塞或者长时间被某个任务占用将不会创建新的线程来执行任务,造成任务队列里的剩余任务被长时间阻塞。
- CachedThreadPool 相比FixedThreadPool更适用于处理要求低延时的场景,不会受线程数量的约束而让新的任务等待。但是如果任务过多会导致开启的线程数量也很多,因此对内存的开销比FixedThreadPool更大,同时多线程调度也会更消耗CPU资源。但是一旦任务被执行完,在等待一段时间后线程会被销毁,因此收缩性较好。
如果以上三种线程不能满足实际业务的需求,可以自定义参数创建更符合实际的ThreadPoolExecutor。