


























当你在面试中流畅地背出线程池的七大参数时,面试官微微一笑,抛出一个灵魂拷问:"那你说说线程池是怎么实现核心线程保活的?非核心线程超时销毁时怎么保证不误杀正在执行任务的线程?" 此时你突然意识到,机械记忆参数的年代早已过去,现在面试官更关注参数背后的设计思想和源码层面的实现逻辑。本文将带你直击线程池最核心的机制,用源码告诉你为什么参数要这样设计。
类比说明:
corePoolSize:常驻值班医生(核心线程)maximumPoolSize:最大可调动的医生总数(含临时抽调)workQueue:候诊区座位(任务队列)handler:当候诊区爆满时的处理策略(拒诊/转院等)核心逻辑:动态资源调度算法,通过源码实现机制解析线程池运作。
addWorker()// ThreadPoolExecutor.java
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 状态检查:线程池是否已关闭等
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN && firstTask == null))
return false;
for (;;) {
int wc = workerCountOf(c);
// 关键判断:是否超过核心数/最大线程数
if (wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
// CAS失败后重试...
}
}
Worker w = new Worker(firstTask);
Thread t = w.thread;
workers.add(w);
t.start(); // 启动新线程
return true;
}
设计亮点:
core参数区分核心/非核心线程workers集合管理所有WorkerrunWorker()final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 允许中断
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock(); // 加锁保证任务执行不被干扰
// 处理线程中断状态...
try {
beforeExecute(wt, task);
task.run(); // 执行任务
afterExecute(task, null);
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly); // Worker退出处理
}
}
关键机制:
getTask()实现任务获取unlock()后允许线程被回收getTask()的玄机private Runnable getTask() {
boolean timedOut = false;
for (;;) {
int c = ctl.get();
// 状态检查(线程池是否已停止)...
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take(); // 核心线程在此阻塞
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
面试必考点:
take()无限阻塞(非自旋)poll(keepAliveTime)核心结构:使用AtomicInteger同时保存线程池状态(高3位)和工作线程数(低29位)。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 状态定义
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
设计精妙之处:
RUNNING -> SHUTDOWN -> STOP -> TIDYING -> TERMINATED)public void setCorePoolSize(int corePoolSize) {
if (corePoolSize < 0)
throw new IllegalArgumentException();
int delta = corePoolSize - this.corePoolSize;
this.corePoolSize = corePoolSize;
if (workerCountOf(ctl.get()) > corePoolSize)
interruptIdleWorkers(); // 中断空闲线程
else if (delta > 0) {
int k = Math.min(delta, workQueue.size());
while (k-- > 0 && addWorker(null, true)) { // 补充核心线程
if (workQueue.isEmpty())
break;
}
}
}
重要启示:
源码级回答:
在getTask()方法中,当判断当前是核心线程(workerCount <= corePoolSize)且未开启allowCoreThreadTimeOut时,会调用workQueue.take()无限阻塞。这种阻塞是通过LockSupport.park()实现的系统级等待,不消耗CPU资源,且只有在队列有新元素时才会被唤醒。
设计思想解析:
shutdown():将状态改为SHUTDOWN,中断所有空闲Worker,继续执行队列中的剩余任务。 shutdownNow():将状态改为STOP,立即中断所有Worker,并返回未处理的任务列表。addWorker逻辑)。 当你真正理解了Worker如何通过AQS实现不可重入锁、状态机转换如何影响任务调度、阻塞队列与线程存活的精妙配合,七大参数将不再是孤立的概念,而是有机组合的设计元素。这才是面试官真正想考察的——对并发编程本质的理解能力。下次面试时,不妨主动反问:"您是想了解参数设计的trade-off,还是具体的实现机制?" 这会让面试官眼前一亮。
除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。