线程池核心线程是如何保持住的?

分类: 365bet现金网 时间: 2025-10-26 06:16:52 作者: admin 阅读: 1170
线程池核心线程是如何保持住的?

概述

本文尝试回答以下几个问题: 1、核心线程池是如何保持住的? 2、当没有任务时,超过核心线程数的线程是如何回收的? 3、线程队列为什么必须是BlockingQueue,普通队列行不行?

背景知识

以下是一些背景知识,方便后面分析问题,如果明白可以跳过,否则建议重温下。

线程参数

corePoolSize:核心线程数。

maximumPoolSize:最大线程数。

keepAliveTime:空闲线程存活时间。

TimeUnit:时间单位。

BlockingQueue:线程池任务队列。

ThreadFactory:创建线程的工厂。

RejectedExecutionHandler:拒绝策略,包括Abort、callRuns,discard,discardOld.

线程池结构

以ThreadPoolExecutor为例,它的结构如下所示: 最重要的两个类就是我们的ThreadPoolExecutor和Worker,其中ThreadPoolExecutor就是线程池本池了,Worker是对Thread的一个简单封装,它实现了Runnable接口,我们可以把它当作线程Thread本身。我们需要知道这些字段的含义是怎样的:

提交任务做了什么?

创建线程池没有任何有价值的东西,就是对ThreadPoolExecutor的属性进行赋值,例如我们刚才提到的那几个重要的属性,所以我们从execute提交任务开始探索内部。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/b8b29d220ece460798819a451e1a34b4.png 当我们创建了线程池,调用execute提交一个Runnable任务,执行流程如下所示: 执行流程 1、任务到来时,看CORE线程数有没有满,如果没有满则新创建线程处理。 2、如果core线程数量满了,则放入队列中。 3、如果队列也满了,则看看线程数量是否达到了maxPoolSize值,如果没有则创建新线程处理。 4、如果已经达到了maxPoolSize的值,则根据拒绝策略处理任务。 所以当有人问execute做了什么时候,可以回答这个执行流程,这是面试时经常问的问题。知道这个流程后,我们再来看下代码是怎么写的(按照惯例,省略了无关代码):

public void execute(Runnable command) {

int c = ctl.get();

if (workerCountOf(c) < corePoolSize) {

// 当线程数量小于核心线程数时,创建新的线程,请注意这里给了command,也就是我们自定义的Runnable任务,后面会再次提到它!

if (addWorker(command, true))

return;

c = ctl.get();

}

if (isRunning(c) && workQueue.offer(command)) {

// 上面没有走到,说明核心线程数量已经满了,则尝试将任务添加到任务队列中,也就是这里的workQueue

int recheck = ctl.get();

if (! isRunning(recheck) && remove(command))

reject(command);

else if (workerCountOf(recheck) == 0)

addWorker(null, false);

}

else if (!addWorker(command, false)) // 添加队列失败,说明队列满了。尝试依赖maxPoolSize创建线程,如果失败则根据拒绝策略处理任务

reject(command);

}

是不清晰多了?不过我们需要继续深挖,看看addWorker()干了什么?第二个boolean参数又是干嘛用的?

private boolean addWorker(Runnable firstTask, boolean core) {

retry:

for (;;) {

// for循环主要通过cas更新线程worker数量

int c = ctl.get(); // 拿到线程数量

for (;;) {

int wc = workerCountOf(c);

if (wc >= CAPACITY ||

wc >=

相关推荐

汽车之家
365皇冠体育网址

汽车之家