定义线程池:
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 10, TimeUnit.DAYS,
new LinkedBlockingDeque<>(5), new ThreadPoolExecutor.AbortPolicy());
执行流程为:创建核心线程 -->入队排队 --> 创建非核心线程 --> 拒绝
![](https://img-blog.csdnimg.cn/img_convert/47dbce655d18cbaa2c9b0d32ab3c21a5.png)
实际举例:
假设当前场景为秒杀,线程池定义如上,在开始后直接进入100个请求。
前2个请求,会先创建2个核心线程。当第3~7个请求来时,先进入阻塞队列排队。当第8~10个请求来时,会创建非核心线程。剩下的82个请求,都将会被拒绝。
假如上述每个请求会打印出各自的序号,则打印结果为:1,2 -- 8,9,10 --3,4,5,6,7
1,为什么执行流程会是这样?原因在ThreadPoolExecutor的execute方法中,如下:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// workerCountOf(c):线程池中所有的线程数
// 1,当所有线程数小于核心线程数时,则创建线程;
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2,当1不满足,即“所有线程数大于核心线程数”,则加入阻塞队列
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,当2也不满足,即“队列也满了”,则创建非核心线程线程数
else if (!addWorker(command, false))
// 4,拒绝策略
reject(command);
}
2,为什么不是先创建2个核心线程,再创建3个非核心线程,再排队5个线程?
实际举例:
拿餐厅举例,当前餐厅有两个大厨(主厨和副厨),每个大厨使用一个灶台,餐厅总共有5个灶台。
今日过节,来了很多客人,前两桌的菜由现有的两位大厨做,接下来的5桌,不是去现招大厨,而是先排队。则当前状况为:2桌用餐,5桌排队。这时又来了10桌客人,老板看到生意好,而且有空余的3个灶台,就现招了3个临时大厨,临时大厨做这10桌客人中先来的3桌,剩余的7桌被告知今日已无法接待,当这3桌做完后,再做排队的客人点的菜。
线程池与当前有个不同的地方需要注意。今日开张时,第一桌客人来了,由主厨做,做完后来了第二桌客人,现实情况还会由主厨做,因为主厨做的好吃。但线程池中是让主厨休息,由副厨做。具体原因在上述代码中第1条if条件满足即可看出,只要线程数量小于核心线程数,不是复用,而是直接创建新的线程。
3,线程池中的线程是如何调用的?
execute方法的addWork方法,即创建了线程,也调用了线程的run方法。
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;
}
}
// 第二部分
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 2.1,新建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()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 2.2,启用线程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
上述代码可分为两个部分。第一个部分是for循环,作用是用来给变量“线程池中的总线程数”加1的,即workerCountOf(c)+1。第二个部分是新建worker,并调用。值得注意的是,线程在线程池中被封装成了Worker(Runnable firstTask),而初始化worker时,会在构造方法里通过线程池工厂创建线程,并将自己当作参数传入。所以上述代码中的2.2,实则是调用worker类的run方法,跟进该run方法得知,又调用了一个runWork的方法,在runWork方法中,找到了线程最熟悉的task.run(),即线程启动。
4,线程池中的线程是如何复用的?
线程复用在最终的runWork方法里,如下 :
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 ((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);
}
}
上述代码的while循环表示,如果Worker有任务,则线程执行任务;如无任务,则通过getTask()从队列中拉取任务执行。复用功能即可达到。
5,当请求逐渐减少后,非核心线程是如何被销毁,只留下核心线程的呢?
请求减少后,线程池不一定按照问题5那样销毁非核心,只留下核心,具体需要看初始化线程池时如何设置参数。这里只讨论个比较容易的逻辑,即如问题5这样。那么是如何实现的,具体在getTask方法中,如下 :
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);
// 1,allowCoreThreadTimeOut默认为false,wc是当前线程池内线程总数
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 2,使用阻塞队列的poll和take来实现线程结束(销毁)还是阻塞等待(复用)
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
如上述代码注释的两个核心逻辑,poll返回null,线程退出,take则是阻塞,等待下一个任务。所以如果将默认allowCoreThreadTimeOut设置为true ,当任务执行完后,将不会再保留线程。