java并发编程之美 学习笔记
线程池主要解决两个问题:
- 当大量执行异步任务时线程池能够提供较好的性能。(不使用线程池时,创建异步任务都需要new线程来执行,而线程的创建和销毁是有一定开销的)。线程池中的线程是可复用的,无需每次执行异步任务都新建和销毁。
- 线程池提供了一种资源限制和管理的手段。如限制线程个数,动态新增线程等。
线程池也提供了许多可调参数和可扩展性接口 ,以满足不同情境的需要,程序
员可以使用更方便的 Executors
的工厂方法,如newCachedThreadPool(线程自动回收),newFixedThreadPool(固定个数线程),newSingleThreadExecutor(单个线程)
.
ThreadPoolExecutor
ThreadPoolExecutor结构
结构1
public class ThreadPoolExecutor extends AbstractExecutorService {
// (高3位)用来表示线程池状态,(低29位)用来表示线程个数
//默认是RUNNING状态,线程个数为 0
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//线程个数掩码位数,
//并不是所有平台 的工的类型都是32位的,所以准确地说, 是具体平台 下 Integer的二进制位数 - 3后的剩余位数所表示的数才是线程的个数
private static final int COUNT_BITS = Integer.SIZE - 3;
//线程最大个数 (低29位)
// 000000000 000000000 000000000 000000001 << 29 = 00100000 000000000 000000000 000000000
// 00100000 000000000 000000000 000000000 -1 = 00011111 11111111 11111111 11111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
/**
* java使用补码的形式表示负数:如 -1:
* 1的原码 00000000 00000000 00000000 00000001
* 1的反码 1111111 11111111 11111111 11111110
* 1的补码(反码末位加1) : 11111111 11111111 111111111 11111111
*
*/
//线程池状态
//(高3位) 11111111 11111111 111111111 11111111 << 29 == 11100000 000000000 000000000 000000000
private static final int RUNNING = -1 << COUNT_BITS;
// (高3位)
// 00000000 000000000 000000000 000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
//(高3位) 00000000 00000000 00000000 00000001 << 29 = 00100000 00000000 00000000 00000000
private static final int STOP = 1 << COUNT_BITS;
// (高3位) 0000000 00000000 00000000 00000010 << 29 = 01000000 00000000 00000000 00000000
private static final int TIDYING = 2 << COUNT_BITS;
// (高3位) 0000000 00000000 00000000 00000011 << 29 = 01100000 00000000 00000000 00000000
private static final int TERMINATED = 3 << COUNT_BITS;
//获取高 3位(运行状态)
// CAPACITY: 00011111 11111111 11111111 11111111
// ~CAPACITY: 11100000 000000000 000000000 000000000
private static int runStateOf(int c) { return c & ~CAPACITY; }
//获取低29位(线程个数)
private static int workerCountOf(int c) { return c & CAPACITY; }
//计算ctl值(线程池状态 | 线程个数)
private static int ctlOf(int rs, int wc) { return rs | wc; }
}
ctl
使用
高3位
表示:线程池状态
低29位
表示:线程个数
线程池状态如下:
RUNNING(111 0~29)
: 接受新任务 && 处理阻塞队列里的任务。SHUTDOWN(000 0~29)
: 拒绝新任务,但是处理阻塞队列里的任务。STOP(001 0~29)
: 拒绝新任务 && 抛弃阻塞队列里任务,同时中断正在处理的任务。TIDYING(010 0~29)
: 所有任务都执行完成(包含阻塞队列[此时为空]),线程池活动线程数为0,将要调用terminated()
方法TREMINATED(011 0~29)
: 终止状态。terminated()
方法执行完后的状态。
线程池状态转换图:
结构2
//用于保存待执行的任务的阻塞队列。
private final BlockingQueue<Runnable> workQueue;
//后续分析.....
private final ReentrantLock mainLock = new ReentrantLock();
private final HashSet<Worker> workers = new HashSet<Worker>();
private final Condition termination = mainLock.newCondition();
private int largestPoolSize;
private long completedTaskCount;
//创建线程的工厂 。
private volatile ThreadFactory threadFactory;
//饱和策略: 当队列满 && 线程个数达到maximumPoolSize后采取的策略。
private volatile RejectedExecutionHandler handler;
//当线程池中线程数量 > 核心线程数, 且线程池中存在idle线程; 表示这些idle线程最大存活时间。
private volatile long keepAliveTime;
/**
* If false :核心线程永远存在(即便核心线程处于idle状态)
* If true : 使用keepAliveTime来控制核心线程数
*/
private volatile boolean allowCoreThreadTimeOut;
// 线程池核心线程个数
private volatile int corePoolSize;
//线程池最大线程数量。
private volatile int maximumPoolSize;
//默认饱和策略 --- AbortPolicy -抛出异常
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
ThreadPoolExecutor构造方法参数说明
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize 保持在池中的线程数量,如果没有设置
allowCoreThreadTimeout
参数,即使这些线程空闲也将一直保持在池中 - maximumPoolSize 线程池中的最大线程数量
- keepAliveTime 当线程池中空闲线程数量超过corePoolSize时,多余的线程会超过这个保活时间会被销毁;
- unit keepAliveTime的单位
- workQueue :任务队列,被添加到线程池中,但尚未被执行的任务;它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;
- threadFactory 线程工厂,用于创建线程,一般用默认即可;
- handler 拒绝策略;当任务太多来不及处理时,如何拒绝任务,有4个策略
ThreadPoolExecutor.AbortPolicy():
抛出java.util.concurrent.RejectedExecutionException
异常ThreadPoolExecutor.CallerRunsPolicy
:直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务ThreadPoolExecutor.DiscardOldestPolicy()
:丢弃最老的任务
-ThreadPoolExecutor.DiscardPolicy
:丢弃当前任务
当通过
execute
提交任务执行时:
- 如果当前线程数量小于corePoolSize,创建新的线程处理任务
- 如果当前线程数量等于corePoolSize,任务加入workQueue队列
- 如果线程池中数量大于等于corePoolSize,workQueue队列满,线程数量小于maximumPoolSize,创建新的线程处理任务
- 如果线程池中线程数量等于maximumPoolSize,workQueue队列也满,根据handler指定的拒绝策略处理任务
- 如果线程池中线程数量大于corePoolSize,空闲线程超过keepAliveTime,线程将被销毁
Executors工具类方法
newSingleThreadExecutor
//java.util.concurrent.Executors
public static ExecutorService newSingleThreadExecutor() {
/**
* coolPoolSize = 1 核心线程个数1
* maximumPoolSize = 1 最大线程个数都为 1
* maximumPoolSize = 0 说明只要线程个数比核心线程个数多并且当前空闲 则 回收。
* LinkedBlockingQueue(): 最大任务数Integer.MAX_VALUE
*/
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
//自定义:threadFactory 创建
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
//创建一个核心线程个数和最大线程个数都为 nThreads 的线程池
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
//自定义线程池--略
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
/**
*
* @param 0 核心线程数 1
* @param Integer.MAX_VALUE 最大线程数 Integer.MAX_VALUE
* @param 60L
* @param TimeUnit.SECONDS 最大空闲时间60s
* @param SynchronousQueue<Runnable>() 同步队列将任务直接提交给线程而不保持它们。
*/
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
}
execute()–添加任务
execute方法的作用是提交任务command到线程池中进行执行。用户提交任务到线程池中的模型如下图:
execute()
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//1.如果线程池线程数 < 核心线程数 ==== 直接添加一个核心线程,并将command作为该线程的第一个任务; --执行成功 return ;
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2. 线程池状态 RUNNING 状态,则将command添加至 workQueue
if (isRunning(c) && workQueue.offer(command)) {
//任务添加成功后,二次检查
int recheck = ctl.get();
//如果线程不处于RUNNING状态,从队列中将任务删除
if (! isRunning(recheck) && remove(command))
//执行决绝策略
reject(command);
// ,如果当前线程池线程数为0,则新增一个线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//3. 当workQueue无法offer任务,则尝试新增一个worker线程,如果新增失败,则执行reject操作;
else if (!addWorker(command, false))
reject(command);
}
execute创建线程分为3步:
- 尝试创建
核心线程
– 成功 return; - 尝试向
任务队列(workQueue)
里添加任务 – 失败,跳转至3 - 尝试新增
worker线程
;
addWorker()
//增加worker
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
//获取线程池状态(高三位)
int rs = runStateOf(c);
//1.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//获取工作线程数量
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
//core == true,表示新增线程为:核心线程
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//CAS操作: 工作线程数+1
if (compareAndIncrementWorkerCount(c))
break retry;
//CAS操作失败,查看线程池状态是否发生变化.
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry; //发生变化,调到外层循环重新执行循环
//未发生变化,继续执行内层U型你换
}
}
//CAS操作执行完毕...
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//创建worker
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();
//将w添加到workers
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
//释放锁
mainLock.unlock();
}
if (workerAdded) {
//启动线程
t.start();
//设置启动标识
workerStarted = true;
}
}
} finally {
//设置启动标识为false
if (! workerStarted)
//添加addWorkerFailed操作, 将w从workers中移除;
addWorkerFailed(w);
}
return workerStarted;
}
1.
/**
* `RUNNING(111 0~29)`: 接受新任务 && 处理阻塞队列里的任务。
* `SHUTDOWN(000 0~29)`:拒绝新任务,但是处理阻塞队列里的任务。
* `STOP(001 0~29)`: 拒绝新任务 && 抛弃阻塞队列里任务,同时中断正在处理的任务。
* `TIDYING(010 0~29)`: 所有任务都执行完成(包含阻塞队列[此时为空]),线程池活动线程数为0
* `TREMINATED(011 0~29)`:终止状态。
*/
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
转换为:
if (rs >= SHUTDOWN &&
(rs != SHUTDOWN ||
firstTask != null ||
workQueue.isEmpty()))
return false;
转换为:
if (rs > SHUTDOWN)
//当前线程池状态为 STOP 、 TIDYING 或 TERMINATED 。
return false;
if (rs == SHUTDOWN && (firstTask != null || workQueue.isEmpty())) {
//当前线程池状态为 SHUTDOWN 并且 firstTask != null ---- 拒绝新任务
//当前线程池状态为 SHUTDOWN 并且任务队列为空。
return false;
}
Worker
Worker继承了AQS,重写了一些列方法。
Worker
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
final Thread thread;
Runnable firstTask;
//记录当前worker,完成的任务数
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); //设置state=-1, 此时不允许interrupt
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
//是否持有独占锁:state=1,表示持有
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;
//state>=0 时,才允许interrupt.
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
t.interrupt();
}
}
}
w.thread.start()
调用run()
,最终调用ThreadPoolExecutor.runWorker()
;
runWorker()
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
boolean completedAbruptly = true;
try {
//从
while (task != null || (task = getTask()) != null) {
//w持有锁
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//异常处理(略....)
//beforeExecute ---空实现
beforeExecute(wt, task);
task.run();
//beforeExecute ---空实现
afterExecute(task, thrown);
} finally {
task = null;
//最后completedTasks++
w.completedTasks++;
//w释放锁
w.unlock();
}
}
completedAbruptly = false;
} finally {
//待任务完成之后,执行清理操作
processWorkerExit(w, completedAbruptly);
}
}
processWorkerExit
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//统计整个线程池的 任务完成数
completedTaskCount += w.completedTasks;
//移除worker
workers.remove(w);
} finally {
mainLock.unlock();
}
//尝试设置为TERMINATE状态
//如采当前是SHUTDONW状态,并且工作队列为空
//或者当前是STOP状态 ,当前线程池里面没有活动线程
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return;
}
//如果workQueue队列不为空 && 线程数< 核心线程数, 则增加线程
addWorker(null, false);
}
}
shutdown()
执行shutdown()
方法后,线程池就不再接受新的任务了,但是workQueue中的任务还是要执行的。
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//1.权限检查
checkShutdownAccess();
//2.尝试将状态设置为SHUTDOWN,若当前状态>=SHUTDOWN,则直接返回;
advanceRunState(SHUTDOWN);
//3. 中断idle的workers ----- 正在执行的线程不会被中断.
interruptIdleWorkers();
//ScheduledThreadPoolExecutor的钩子函数,在这里为空实现;
onShutdown();
} finally {
mainLock.unlock();
}
//4.尝试设置为TERMINATE状态.
tryTerminate();
}
shutdownNow()
调用shutdownNow()方法后,线程池不再接受新的任务,并丢失任务队列中的任务,正在执行的任务会被中断,该方法会立即返回。返回值为队列里被丢弃的任务列表。
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
//线程池状态设置为STOP
advanceRunState(STOP);
//中断所有worker线程 ----- 正在执行的线程依然会被中断
interruptWorkers();
//将workQueue中的任务移动至tasks列表;
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
总结
线程池巧妙地使用一个 Integer 类型的原子变量来记录线程池状态和线程池中的线程个数。
通过线程池状态来控制任务的执行,每个 Worker 线程可以处理多个任务。
线程池通过线程的复用减少了线程创建和销毁的开销。