并发编程(二)------线程池
什么是线程池
线程池(Thread Pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,对线程统一管理。
线程池就是存放线程的池子,池子里存放了很多可以复用的线程。
创建线程和销毁线程的花销是比较大的(手动new Thread类),创建和消耗线程的时间有可能比处理业务的时间还长。这样频繁的创建线程和销毁线程是比较消耗资源的。(我们可以把创建和销毁的线程过程去掉)。
使用线程池的优势
- 提高效率,创建好一定数量的线程放在池中,等需要使用的时候就从池中拿一个,这要比需要的时候创建一个线程对象要快的多。
- 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
- 提高系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;
创建线程池的几种方式
总体来说线程池的创建可以分为以下两大类:
1.通过ThreadPoolExecuter手动创建线程池
2.通过Executors执行器自动创建线程池
线程池的几种状态
通过线程池源码查看可以发现由ctl属性来标识当前线程池的状态。
状态总共有五种
- RUNING
- SHUTDOWN
- STOP
- TIDYING
- TERMINATED
通过32bit标识,高3位标识当前线程执行状态,低29位标识线程总数。
通过为位移运算可以得出,当高三位为111时,线程池为RUNING状态,000为SHUTDOWN,001为STOP,010为TIDYING状态,011为TERMINATED.
// ctl标识了两种状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 高三位为线程池状态
// 低29位为线程池中工作线程总数
private static final int COUNT_BITS = Integer.SIZE - 3;
// 标识工作线程最大数量
// 00011111 11111111 11111111 11111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 111 左位移29位
// 11111111 11111111 11111111 11111111
// 11100000 00000000 00000000 00000000
private static final int RUNNING = -1 << COUNT_BITS;
// 000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 001
private static final int STOP = 1 << COUNT_BITS;
// 010
private static final int TIDYING = 2 << COUNT_BITS;
// 011
private static final int TERMINATED = 3 << COUNT_BITS;
// 计算出当前线程池的状态
// 00000000 00000000 00000000 00000000
// 00011111 11111111 11111111 11111111
// &运算后值
// 11100000 00000000 00000000 00000000
// ~取反
// 00011111 11111111 11111111 11111111
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 计算出当前线程池中工作线程的个数
// 00000000 00000000 00000000 00000001
// 00011111 11111111 11111111 11111111
// &运算后值
// 00000000 00000000 00000000 00000001
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
状态详解
状态名称 | 描述 |
---|---|
RUNING | 运行中状态,可以接受新的任务和执行队列中的任务 |
SHUTDOWN | 需要手动调用shutdown()方法关闭状态,不在接受新的任务,但是会把任务队列中的任务执行完,调用shutdownNow方法流转至STOP状态 |
STOP | 停止状态,不在接受新的任务,也不会在执行任务队列中的任务,并且会把正在工作中的线程中断销毁。手动调用shutdownNow()方法流转至该状态 ,工作线程销毁完后流转至TIDIYING中间状态 |
TIDYING | 整理中状态,所有任务都已经执行完毕,工作线程数为0,过渡到此状态的工作线程会调用钩子方法tedminated() |
TERMINATE | 终止状态,钩子方法terminated()执行完毕 |
execute方法源码
public void execute(Runnable command) {
//执行非空判断
if (command == null)
throw new NullPointerException();
//获取ctl属性
int c = ctl.get();
//判断当前工作线程个数是否小于核心线程数大小
if (workerCountOf(c) < corePoolSize) {
// 如果小于的话添加核心线程执行command任务
if (addWorker(command, true))
//添加成功直接返回
return;
// 添加失败
// 重新获取ctl属性,此处可能存在并发情况导致ctl属性变更
c = ctl.get();
}
// 添加核心线程失败的情况
// 判断当前线程池是否是RUNNING状态,并且将任务添加到工作队列中等待执行
// 添加核心线程失败有可能是线程池状态发生变化导致,因此需要判断当前线程池状态
if (isRunning(c) && workQueue.offer(command)) {
// 添加任务到工作队列成功
// 此处无法保证原子性,可能存在并发安全问题
// 再次获取ctl值
int recheck = ctl.get();
// 可能发生当前线程添加任务成功后,其他线程修改了线程池状态
// 判断当前线程池是否非RUNNING状态,如果是非RUNNING状态,那么当前线程池处于在被关闭的过程中,需要移除当前添加的任务
if (! isRunning(recheck) && remove(command))
// 如果是非RUNNING状态,且删除当前任务成功,执行拒绝策略
reject(command);
// 当前线程池状态为RUNNING状态,或者删除任务失败
// 判断当前工作线程是否为0
else if (workerCountOf(recheck) == 0)
// 工作线程数为0,但此时工作队列中有任务在排队
// 创建一个空任务非核心线程来处理工作队列中的任务
addWorker(null, false);
}
// 任务添加到工作队列失败,当前工作队列已满,尝试创建非核心线程执行command任务
// addWorker创建成功返回ture,此处取反为创建失败的情况
else if (!addWorker(command, false))
// 创建非核心线程失败,执行拒绝策略
reject(command);
}
addWorker方法源码
private boolean addWorker(Runnable firstTask, boolean core) {
//对线程池状态的判断,以及对工作线程数量的判断
// retry: 外层for循环的标识
retry:
for (;;) {
// 获取ctl的值
int c = ctl.get();
// 获取当前线程池的状态
int rs = runStateOf(c);
// 只有不为RUNNING状态的值才会>=SHUTODWN
// 那么只要是RUNNING状态该if判断则为false
if (rs >= SHUTDOWN &&
// 到这里说明当前线程是非RUNNING状态,判断当前提交的任务是否可以不处理
// 如果当前状态为SHUTDOWN并且提交的任务为空,工作队列不为空,说明本次提交的任务是需要处理的
// rs == SHUTDOWN 为false,说明当前线程池为STOP或者后续状态,则直接返回false不处理本次提交的任务
! (rs == SHUTDOWN &&
// 当前线程池为SHUTDOWN状态,并且提交任务为空,工作队列不为空
// 说明当前执行的是addWorker(null,false)方法,需要处理工作队列的任务
firstTask == null &&
! workQueue.isEmpty()))
// 满足return false的情况
// 当前线程池状态为非RUNNING状态,并且状态不为SHUTDOWN,说明当前线程池状态可能为STOP,TIDYING,TERMINATE
// 当前线程池状态为非RUNNING状态,状态为SHUTDOWN,提交的任务不为null
// 当前线程池状态为非RUNNING状态,状态为SHUTDOWN,提交的任务为null,但是工作队列为空
return false;
// 到这里说明当前线程状态为RUNNING或者SHOTDOWN
// 当前任务任务可能不为null或者不为null
// 工作队列中可能存在或者不存在任务
for (;;) {
// 基于获取到的ctl获取当前工作线程数量
int wc = workerCountOf(c);
// 判断工作线程是否大于最大值
if (wc >= CAPACITY ||
// 如果需要创建的是核心线程,是否大于设置的corePoolSize
// 如果是非核心线程,是否大于设置的maximumPoolSize
wc >= (core ? corePoolSize : maximumPoolSize))
// 大于直接返回创建失败
return false;
// 基于CAS操作对工作线程数+1
if (compareAndIncrementWorkerCount(c))
// 如果成功跳出外层for循环
break retry;
// 如果失败了说明有并发操作,当前线程池状态和工作线程数可能发生变化
// 重新读取ctl值
c = ctl.get(); // Re-read ctl
// 判断重新获取的ctl值线程状态是否和之前获取到的一致
if (runStateOf(c) != rs)
// 如果不一致则跳出本次循环,重新判断状态
continue retry;
}
}
//添加工作线程并启动
// 工作线程启动标识
boolean workerStarted = false;
// 工作线程添加成功标识
boolean workerAdded = false;
// 工作线程对象
Worker w = null;
try {
// new Worker()对象,并且将当前任务传入
w = new Worker(firstTask);
// 获取到worker对象中绑定的thread线程
final Thread t = w.thread;
// 如果t不为null,健壮性判断
if (t != null) {
// 线程池中的全局锁
final ReentrantLock mainLock = this.mainLock;
// 通过AQS上锁
mainLock.lock();
try {
// 重新获取ctl用于判断状态
// 避免在获取到锁前当前线程池状态被修改
int rs = runStateOf(ctl.get());
// 只有RUNNING状态会小于SHUTDOWN,如果为RUNNING则不进行后续判断直接为ture
if (rs < SHUTDOWN ||
// 如果当前状态为SHOTDOWN并且firstTask为null,说明当前为addWorker(null,false),需要创建工作线程
(rs == SHUTDOWN && firstTask == null)) {
//预检查,健壮性判断,判断当前线程是否处于run状态
if (t.isAlive())
throw new IllegalThreadStateException();
// 将当前创建好的worker对象扔到workers中
workers.add(w);
// 获取工作线程Set大小
int s = workers.size();
// 判断当前工作线程数是否大于历史最大工作线程数
if (s > largestPoolSize)
// 大于则记录当前工作线程数为历史最大值
largestPoolSize = s;
// 设置工作线程添加成功标识
workerAdded = true;
}
} finally {
// 锁释放
mainLock.unlock();
}
// 如果当前工作线程添加成功
if (workerAdded) {
// 启动工作线程Thread
t.start();
// 启动成功标识设置
workerStarted = true;
}
}
} finally {
// 如果当前工作线程启动失败
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
// 启动工作线程失败调用该方法,传入创建的工作线程
private void addWorkerFailed(Worker w) {
// 获取全局锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 如果当前工作线程不为null
if (w != null)
// 从工作线程集合中删除该工作线程
workers.remove(w);
// 对工作线程数-1
decrementWorkerCount();
// 尝试将线程池状态修改为TIDYING
tryTerminate();
} finally {
mainLock.unlock();
}
}