目录:
- 1. 为什么要使用线程池?
- 2. 线程池的运行原理。
- 3. 自己实现一个线程池。
- 4. 几种常见的线程池。
- 5. 线程池的源码分析。
1. 为什么要使用线程池?
前面的文章我有讲过Java中的线程知识:《Java篇 - 线程总结(总有你想知道的)》
https://blog.csdn.net/u014294681/article/details/85231356
那为什么要使用线程池?
- 为了减少创建和销毁线程的次数,让每个线程可以多次使用,线程创建和销毁的开销比较大,大过了线程空转的开销。
- 可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致系统崩溃。
- 提高响应速度,当任务到达时,任务可以不需要等到线程创建就能立即执行。
2. 线程池的运行原理
先简单看下线程池的构造器:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
- corePoolSize:核心池的大小,在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到队列当中。
- maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程。
- keepAliverTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。
- unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
- TimeUnit.DAYS;
- TimeUnit.HOURS;
- TimeUnit.MINUTES;
- TimeUnit.SECONDS;
- TimeUnit.MILLISECONDS;
- TimeUnit.MICROSECONDS;
- TimeUnit.NANOSECONDS;
- workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:
- ArrayBlockingQueue;
- LinkedBlockingQueue;
- SynchronousQueue(一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有);
- threadFactory:线程工厂,主要用来创建线程。
- handler:超出线程范围和队列容量的任务的处理程序。
整理流程如下:
- 如果线程池中的线程数量少于corePoolSize(核心线程数量),那么会直接开启一个新的核心线程来执行任务,即使此时有空闲线程存在。
- 如果线程池中线程数量大于等于corePoolSize(核心线程数量),那么任务会被插入到任务队列中排队,等待被执行,此时并不添加新的线程。
- 如果在步骤2中由于任务队列已满导致无法将新任务进行排队,这个时候有两种情况:
- 线程数量未达到maximumPoolSize(线程池最大线程数),立刻启动一个非核心线程来执行任务。
- 线程数量已达到maximumPoolSize(线程池最大线程数),拒绝执行此任务,ThreadPoolExecutor会通过RejectedExecutionHandler,抛出RejectExecutionException异常。
3. 自己实现一个线程池
根据上面的原理,我带大家来实现一个简单的线程池,这也是面试中手撸code的常见题。
public class TestThreadPoolExecutor {
/* 存放线程的集合 */
private final ArrayList<MyThead> threads;
/* 任务队列 */
private final BlockingQueue<Runnable> taskQueue;
/* 线程池限定大小 */
private final int threadNum;
/* 已经工作的线程数目 */
private int workThreadNum;
private volatile boolean isShutDown;
private final ReentrantLock mainLock = new ReentrantLock();
TestThreadPoolExecutor(int threadNum) {
threads = new ArrayList<>(threadNum);
taskQueue = new ArrayBlockingQueue<>(threadNum);
this.threadNum = threadNum;
workThreadNum = 0;
}
void execute(Runnable runnable) {
if (isShutDown)
throw new RuntimeException("TestThreadPoolExecutor is shutdown");
try {
mainLock.lock();
if (workThreadNum < threadNum) {
// 线程池未满,每加入一个任务则开启一个线程
MyThead myThead = new MyThead(runnable);
myThead.start();
threads.add(myThead);
workThreadNum++;
} else {
// 线程池已满,放入任务队列,等待有空闲线程时执行,如队列已满,无法添加时,则拒绝任务
if (!taskQueue.offer(runnable)) {
System.out.println("Task queue is full!");
}
}
} finally {
mainLock.unlock();
}
}
void shutDown() {
for (Thread thread : threads) {
thread.interrupt();
}
isShutDown = true;
}
class MyThead extends Thread {
private Runnable task;
MyThead(Runnable runnable) {
this.task = runnable;
}
@Override
public void run() {
// 该线程一直启动着,不断从任务队列取出任务执行
while (!Thread.currentThread().isInterrupted()) {
// 如果初始化任务不为空,则执行初始化任务
if (task != null) {
task.run();
task = null;
} else {
Runnable queueTask = taskQueue.poll();
if (queueTask != null)
queueTask.run();
}
}
}
}
public static void main(String[] args) {
TestThreadPoolExecutor threadPoolExecutor = new TestThreadPoolExecutor(5);
for (int i = 0; i < 20; i++) {
threadPoolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName() + " is running");
});
}
}
}
执行输出:
Thread-0 is running
Thread-2 is running
Thread-3 is running
Thread-1 is running
Thread-1 is running
Thread-1 is running
Thread-4 is running
Thread-0 is running
Thread-4 is running
Thread-4 is running
Thread-2 is running
Thread-1 is running
Task queue is full!
Thread-4 is running
Thread-0 is running
Thread-4 is running
Thread-1 is running
Thread-2 is running
Thread-3 is running
Thread-0 is running
4. 几种常见的线程池
上面大概分析了下线程池的运行机制和自己实现了一个简单的线程池。
接下来看看几种常见的线程池,Executors是JDK提供的线程池工厂类:
public class Executors {
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
private Executors() {}
}
- 4.1 newFixedThreadPool(int nThreads)
创建一个定长线程池,核心线程数和最大线程数都是nThreads,超时时间为0。可控制线程最大并发数,超出的线程会在队列中等待。定长线程池的大小最好根据系统资源进行设置,如Runtime.getRuntime().availableProcessors()。
比如:
ExecutorService fixedPool = Executors.newFixedThreadPool(3);
其中,
- 最多3个线程将处于活动状态。
- 如果提交了3个以上的线程,那么它们将保持在队列中,直到线程可用。
- 如果一个线程由于执行关闭期间的失败而终止,则执行器尚未被调用,会创建一个新线程。
- 线程会一直存在,直到池关闭。
看一个例子:
public static void main(String[] args) {
testFixedThreadPool();
}
private static void testFixedThreadPool() {
ExecutorService executor = Executors.newFixedThreadPool(3);
// Cast the object to its class type
ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;
//Stats before tasks execution
System.out.println("Core threads: " + pool.getCorePoolSize());
System.out.println("Largest executions: " + pool.getLargestPoolSize());
System.out.println("Maximum allowed threads: " + pool.getMaximumPoolSize());
System.out.println("Current threads in pool: " + pool.getPoolSize());
System.out.println("Currently executing threads: " + pool.getActiveCount());
System.out.println("Total number of threads(ever scheduled): " + pool.getTaskCount());
executor.submit(new Task());
executor.submit(new Task());
System.out.println("Core threads: " + pool.getCorePoolSize());
System.out.println("Largest executions: "
+ pool.getLargestPoolSize());
System.out.println("Maximum allowed threads: "
+ pool.getMaximumPoolSize());
System.out.println("Current threads in pool: "
+ pool.getPoolSize());
System.out.println("Currently executing threads: "
+ pool.getActiveCount());
System.out.println("Total number of threads(ever scheduled): "
+ pool.getTaskCount());
executor.shutdown();
}
private static class Task implements Runnable {
public void run() {
try {
Long duration = (long) (Math.random() * 5);
System.out.println("Running Task! Thread Name: " + Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(duration);
System.out.println("Task Completed! Thread Name: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行输出:
Core threads: 3
Largest executions: 0
Maximum allowed threads: 3
Current threads in pool: 0
Currently executing threads: 0
Total number of threads(ever scheduled): 0
Core threads: 3
Largest executions: 2
Maximum allowed threads: 3
Current threads in pool: 2
Currently executing threads: 2
Total number of threads(ever scheduled): 2
Running Task! Thread Name: pool-1-thread-2
Running Task! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-1
Task Completed! Thread Name: pool-1-thread-2
- 4.2 newSingleThreadExecutor()
创建一个单线程化的线程池,核心线程数和最大线程数都是1,超时时间为0。它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果我想要实现一个只有一个运行线程的线程池,直接new一个ThreadPoolExecutor如:new ThreadPoolExecutor(1, 0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())可不可以呢?答案是:不可以,因为在ThreadPoolExecutor的构造函数里第一个if语句就判断了,如果maximumPoolSize <= 0就会抛出一个异常。
- 1. 第一次submit一个线程,因为没有在运行的线程,所以该线程不会被提交到阻塞队列里,而是直接start运行。
- 2. 紧接着我们再submit一个线程,这次因为已经有一个线程在运行了,并且运行的线程数量等于corePoolSize所以,这个线程会被提交到阻塞队列。
- 3. 再submit第三个线程,这次就是饱和策略大显身手的时候了。
private static void testSingleThreadExecutor() {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
执行输出:
0
1
2
3
4
5
6
7
8
9
- 4.3 newCachedThreadPool()
创建一个可缓存的线程池,核心线程数为0,最大线程数为Integer.MAX_VALUE,超时时间为60秒,队列为SynchronousQueue(不存储任务,所以提交任务立马就能执行)。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小,所以使用时要注意。
private static void testCachedThreadPool() {
ExecutorService pool = Executors.newCachedThreadPool();
Thread t1 = new MyThread();
Thread t2 = new MyThread();
Thread t3 = new MyThread();
Thread t4 = new MyThread();
Thread t5 = new MyThread();
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.shutdown();
}
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
}
}
执行输出:
pool-1-thread-2 is running
pool-1-thread-4 is running
pool-1-thread-3 is running
pool-1-thread-1 is running
pool-1-thread-5 is running
- 4.4 newScheduledThreadPool(int corePoolSize)
创建一个可以控制线程池内的线程定时或周期性执行某任务的线程池。
private static void testScheduledThreadPool() {
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
long initialDelay1 = 1;
long period1 = 1;
service.scheduleAtFixedRate(
new ScheduledTask("job1"), initialDelay1,
period1, TimeUnit.SECONDS);
long initialDelay2 = 1;
long delay2 = 1;
service.scheduleWithFixedDelay(
new ScheduledTask("job2"), initialDelay2,
delay2, TimeUnit.SECONDS);
}
private static class ScheduledTask implements Runnable {
private final String jobName;
public ScheduledTask(String jobName) {
super();
this.jobName = jobName;
}
@Override
public void run() {
System.out.println("execute " + jobName);
}
}
执行输出:
execute job1
execute job2
execute job1
execute job2
execute job1
execute job2
execute job1
execute job2
execute job1
execute job2
execute job1
execute job2
execute job1
...
5. 线程池的源码分析
- 5.1 构造器
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
构造器可以看出:
- 核心线程数不能小于0。
- 最大线程数不能为负数。
- 最大线程数不能小于核心线程数。
- 保活时间不能为负数。
- 队列,线程工厂,饱和策略不能为空。
- 5.2 提交任务
public Future<?> submit(Runnable task) {
// task不能为空,否则抛出异常
if (task == null) throw new NullPointerException();
// 构造一个RunnableFuture
RunnableFuture<Void> ftask = newTaskFor(task, null);
// 执行任务
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
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);
}
提交任务流程分3步:
- 1. 如果运行的线程数少于corePoolSize,则尝试启动一个新线程去执行该任务。对addworker的调用会自动检查线程池的runState和workerCount,如果添加失败会返回false,添加成功则直接返回。
- 2. 如果运行的线程数大于corePoolSize,则尝试插入队列,这时仍然需要再次检查线程池的runState,因为可能进入此分支后线程池关闭了。如果线程池已关闭,会移除该任务,并交给饱和策略处理,如果没有,则启动一个新线程。
- 3. 如果前2步都不是,则尝试添加一个新线程。如果添加失败(可能是线程池已关闭或者饱和),则交给饱和策略处理。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
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 {
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;
}
- 获取当前线程池的状态,如果是STOP, TIDYING, TERMINATED状态的话,则会返回false,如果现在状态是SHUTDOWN,但是firstTask不为空或者workQueue为空的话,那么直接返回false。
- 通过自旋的方式,判断要添加的Worker是否是corePool,如果是的话,那么则判断当前的workerCount是否大于corePoolsize,否则则判断是否大于maximumPoolSize,如果满足的话,说明workerCount超出了线程池大小,直接返回false。如果小于的话,那么判断是否成功将WorkerCount通过CAS操作增加1,如果增加成功的话。则进行到第3步,否则则判断当前线程池的状态,如果现在获取到的状态与进入自旋的状态不一致的话,那么则通过
continue retry
重新进行状态的判断。 - 如果满足了的话,那么则创建一个新的Worker对象,然后获取线程池的重入锁后,判断当前线程池的状态,如果当前线程池状态为STOP, TIDYING, TERMINATED的话,那么调用decrementWorkerCount将workerCount减一,然后调用tryTerminate停止线程池,并且返回false。
- 如果状态满足的话,那么则在workers中将新创建的worker添加,并且重新计算largestPoolSize,然后启动Worker中的线程开始执行任务。
- 重新Check一次当前线程池的状态,如果处于STOP状态的话,那么就调用interrupt方法中断线程执行。
任务添加成功会加入到workers中,workers就是用来负责管理所有线程任务的:
private final HashSet<Worker> workers = new HashSet<Worker>();
然后调用start()启动任务:
if (workerAdded) {
t.start();
workerStarted = true;
}
线程池使用的是自旋锁来判断线程池的运行状态:
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; }
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
/**
* Attempts to CAS-increment the workerCount field of ctl.
*/
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
/**
* Attempts to CAS-decrement the workerCount field of ctl.
*/
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
/**
* Decrements the workerCount field of ctl. This is called only on
* abrupt termination of a thread (see processWorkerExit). Other
* decrements are performed within getTask.
*/
private void decrementWorkerCount() {
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
- 5.3 Worker类
上面提交任务最终提交的是Worker,那么Worker是怎么定义的呢?
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
可以看到Worker类接收一个Runnable对象,里面提供了操作锁的方法,和执行线程任务的方法。
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);
}
}
runWorker就是一个死循环,先执行传进来的任务,执行完后,又不断地从任务队列里获取任务去执行,这也能解释,为什么线程池能保持核心线程不死。
- 5.4 线程池如何回收线程?
在runWorker方法中,有一个无限循环,不断地从队列中取任务,如果获取到null,则for循环退出,线程也就终止了:
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
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会一直去拿任务,属于核心线程的会一直阻塞在 workQueue.take()方法,直到拿到Runnable然后返回,非核心线程会workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) ,如果超时还没有拿到,下一次循环判断compareAndDecrementWorkerCount就会返回null,Worker对象的run()方法循环体的判断为null,任务结束,然后线程被系统回收。当然如果设置了allowCoreThreadTimeOut,那么核心线程也将会被回收。
- 5.5 线程池关闭
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
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()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
shutdown会将workers中的线程都中断。
- 5.6 线程池的锁
线程池的锁只能保证线程池管理的线程的增删改查操作是线程安全的,线程执行的任务逻辑是非线程安全的,所以如果要保证执行任务的线程安全,需要开发者自行处理同步。
- 5.7 线程池为何要使用阻塞队列?
线程池创建线程需要获取mainlock这个全局锁,影响并发效率,阻塞队列可以很好的缓冲。另外一方面,如果新任务的到达速率超过了线程池的处理速率,那么新到来的请求将累加起来,这样的话将耗尽资源。