在线程池中活跃线程数达到corePoolSize时,线程池将会将后续的task提交到BlockingQueue中,为什么这样设计呢?
原因为:
-
线程池创建线程需要获取mainlock这个全局锁,影响并发效率,阻塞队列可以很好的缓冲。
-
如果新任务的到达速率超过了线程池的处理速率,那么新到来的请求将累加起来,这样的话将耗尽资源。
在一个task提交到线程池时,假设可以被线程池中的一个线程执行,则进行以下过程:
exeute —》addWorker(Runnable command, boolean core)—》workers.add(w),启动线程执行任务(获取全局锁ReentrantLock mainLock)
具体源码如下:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//如果当前正在运行的线程数小于corePoolSize,则创建新的线程
//执行当前任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果当前运行的线程数大于等于corePoolSize或者线程创建失败
//则把当前任务放入工作队列
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);
}
//如果线程池任务无法加入到工作队列(说明工作队列满了)
//创建一个线程执行任务。如果新创建后当前运行的线程数大于
//maximumPoolSize则拒绝执行任务
else if (!addWorker(command, false))
reject(command);
}
private boolean addWorker(Runnable firstTask, boolean core){
//省略部分代码
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();
//加入Worker数组
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;
}
上述代码中:
w = new Worker(firstTask);
final Thread t = w.thread;
Worker实现了Runnable接口,里面定义了一个final变量Thread thread
Worker的构造函数为:
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
run函数为:
public void run() {
runWorker(this);
}
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();
try {
beforeExecute(wt, task);
Throwable thrown = null;
task.run();
afterExecute(task, thrown);
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
看完上述代码后,我们可以得出 线程池中为什么要使用阻塞队列 的原因:
- 线程池创建线程需要获取mainlock这个全局锁,影响并发效率,阻塞队列可以很好的缓冲。
- 另外一方面,如果新任务的到达速率超过了线程池的处理速率,那么新到来的请求将累加起来,这样的话将耗尽资源。
转载自:https://blog.csdn.net/qq_35181209/article/details/77921029