餐盘在灯光的照耀下格外晶莹洁白,女友拿起红酒杯轻轻地抿了一小口,对我说:“常常听你说线程池,到底线程池究竟是个什么原理?”我楞了一下,内心想女友今天是怎么了,怎么忽然问出这么专业的问题,但作为一个专业人士在女友面前也不能露怯啊,想了一下便说:“我先给你讲讲我前同事老王的故事吧!”程序员
老王是一个已经北漂十多年的程序员,岁数大了,加班加不动了,升迁也无望,因而拿着手里的一些积蓄,回老家转行创业。他选择了洗浴行业,开一家洗浴中心,是的,一家正规的洗浴中心。以前在北京的时候,喜欢去的澡堂叫“清华池”,他想了想,就给本身的洗浴中心取名为“线程池”。面试
线程池开业之后,老王发现有顾客想作足疗,因而就招聘了1个足疗技师,多增长了一项业务增长了收入。随着作足疗的顾客增多,为了赚更多钱又招聘了4个足疗技师。 过了一段时间,洗浴中心的生意愈来愈好,作足疗的顾客也愈来愈多。可是,老王发现本身店里的足疗技师已经有5个足疗技师,再招聘就太多了,支付不起再多工资了。足疗技师忙不过来怎么办?老王是个聪明人,立刻想到办法:让顾客排队,有哪一个足疗技师作完了,空闲出来了,就在队伍里再叫一个顾客继续作。bash
一到周末,来洗浴中心的顾客比平时多了几倍,想足疗的顾客排队时间太长,顾客们已经不耐烦了。老王立刻作出反应,又紧急从其余洗浴中心招聘了5个足疗技师,为队伍里顾客作足疗,大大减小排队的顾客。 不过,有时生意太火爆了,紧急招聘的技师也用上了,顾客排队时间也是很长,再来新的顾客,老王只能满脸赔笑地和顾客说:“您下次再来吧,下次给您找个好技师。”,把顾客拒之门外。 过了周末之后,店里不能养闲人啊,老王就把紧急招聘的技师都辞退了。ui
老王的生意越作越红火,很快就要开分店、融资上市、走上人生巅峰。既然这么成功,就让咱们来复盘一下他的经营之道吧。this
若是你了解了老王的经营之道,线程池就不难理解了,把顾客替换成任务,把足疗技师替换成线程,线程池洗浴中心就是线程池了,线程池的内部原理就是这样的:spa
铃铃铃,闹铃把我吵醒,原来是一场梦啊,我哪有什么女友?今天上午有一个面试,赶忙起床洗漱完毕,就出发了。在路上回想那个奇怪的梦,要再也不复习一下线程池的内部原理吧! 先看一下ThreadPoolExecutor类的execute方法:线程
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//获取clt,clt记录着线程池状态和运行线程数。
int c = ctl.get();
//运行线程数小于核心线程数时,建立线程放入线程池中,而且运行当前任务。
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
//建立线程失败,从新获取clt。
c = ctl.get();
}
//线程池是运行状态而且运行线程大于核心线程数时,把任务放入队列中。
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//从新检查线程池不是运行状态时,
//把任务移除队列,并经过拒绝策略对该任务进行处理。
if (! isRunning(recheck) && remove(command))
reject(command);
//当前运行线程数为0时,建立线程加入线程池中。
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//运行线程大于核心线程数时而且队列已满时,
//建立线程放入线程池中,而且运行当前任务。
else if (!addWorker(command, false))
//运行线程大于最大线程数时,失败则拒绝该任务
reject(command);
}
复制代码
在execute方法中,屡次调用的addWorker方法,再看一下这个方法:code
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
//获取clt,clt记录着线程池状态和运行线程数。
int c = ctl.get();
//获取线程池的运行状态。
int rs = runStateOf(c);
//线程池处于关闭状态,或者当前任务为null
//或者队列不为空,则直接返回失败。
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//获取线程池中的线程数
int wc = workerCountOf(c);
//线程数超过CAPACITY,则返回false;
//这里的core是addWorker方法的第二个参数,
//若是为true则根据核心线程数进行比较,
//若是为false则根据最大线程数进行比较。
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//尝试增长线程数,若是成功,则跳出第一个for循环
if (compareAndIncrementWorkerCount(c))
break retry;
//若是增长线程数失败,则从新获取ctl
c = ctl.get();
//若是当前的运行状态不等于rs,说明状态已被改变,
//返回第一个for循环继续执行
if (runStateOf(c) != rs)
continue retry;
}
}
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();
//把刚刚建立的线程加入到线程池中
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;
}
复制代码
一个中年男子坐在我面前,对我说:“您好,我是今天的面试官。”我微笑地回应:“您好。”面试官面无表情地问我:“线程池必定用过吧,能说说线程池的内部原理嘛?”我差点笑出声来,自信满满地说……cdn