当项目中有频繁创建线程的场景时,往往会用到线程池来提高效率。所以,线程池在项目开发过程中的出场率是很高的。
那线程池是怎么工作的呢?它什么时候创建线程对象,如何保证线程安全...
当实例化线程池对象时,并没有预先创建corePoolSize
个线程对象,而是在调用execute
或submit
提交任务时,才会创建线程对象。
public void execute(Runnable command) {
int c = ctl.get();
// 1. 如果核心线程数没有用完,则让核心线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2. 核心线程用完后,尝试添加到等待队列
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);
}
// 3. 等待队列满后,尝试创建临时线程执行任务;
// 如果创建临时线程失败,即达到了最大线程数,则采用拒绝策略处理
else if (!addWorker(command, false))
reject(command);
}
// 线程池采用状态压缩的思想,通过 32 个 bit 位来存储线程池状态和工作线程数量
// 其中,高 3 位存储线程池状态,低 29 位存储工作线程数量
// 表示工作线程数量位数
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程池最大容量
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 通过线程安全的 atomic 对象来存储线程池状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int ctlOf(int rs, int wc) { return rs | wc; }
1、对于一些需要线程共享的成员变量,通过volatile
修饰
2、线程池状态和工作线程数的修改通过AtomicInteger
类自带的线程安全方法实现
3、工作线程Worker
通过线程不安全的HashSet
存储,每次操作HashSet
时都通过一个可重入锁ReentrantLock
保证线程安全,ReentrantLock
还用来保证访问一些线程不安全的变量,比如
/**
* Tracks largest attained pool size. Accessed only under
* mainLock.
*/
private int largestPoolSize;
/**
* Counter for completed tasks. Updated only on termination of
* worker threads. Accessed only under mainLock.
*/
private long completedTaskCount;
4、Worker
实现了AQS
类,保证线程安全地操作工作线程Worker
。
原文:https://www.cnblogs.com/flowers-bloom/p/15139586.html