线程池笔记

jdk1.8源码中的UML

ThreadPoolExecutor
AbstractExecutorService
ExecutorService
Executor
接口Executor

此接口提供了一种将任务提交与每个任务的运行机制分离的方法

void execute(Runnable command);
接口ExecutorService
  1. 提供两个关闭ExecutorService的方法。关闭后,没有在执行的任务,也没有等待执行的任务且新任务无法提交。此时ExecutorService资源将被回收。

    • shutdown方法,关闭前会执行已提交的任务
    • shutdownNow方法,会阻止已提交等待执行的任务,并且会尝试关闭正在执行的任务。
  2. 扩充执行任务的能力。

    • submit方法,future方式用来等待任务执行结果
    • invokeAny方法,执行任何一个任务
    • invokeAll方法,执行一批任务
抽象类AbstractExecutorService

提供ExecutorService执行方法的默认实现,将执行任务的流程串起来,下层只需关注任务的实现。

ThreadePoolExecutor

实现最复杂的运行部分,一方面
维护自身的生命周期,另一方面同时管理线程和任务,使两者良好的结合从而执行并行任务。

ThreadePoolExecutor运行机制

模型图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pg9VCUOI-1655452866388)(https://github.com/wangjinwengithub/-/blob/master/%E7%BA%BF%E7%A8%8B%E6%B1%A0/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E6%A8%A1%E5%9E%8B.drawio.png?raw=true)]

线程池5种状态
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
    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;//所有任务都已终止,workCount(有效线程数)为0
    private static final int TERMINATED =  3 << COUNT_BITS;//terminated()方法执行完以后变成该状态
    

RUNNING为-1左移29位。即:
1000 0000 0000 0000 0000 0000 0000 0001 左移29位得
1001 0000 0000 0000 0000 0000 0000 0000
SHUTDOWN为0左移29位。即:
0000 0000 0000 0000 0000 0000 0000 0000 左移29位得
0000 0000 0000 0000 0000 0000 0000 0000
STOP为1左移29位。即:
0000 0000 0000 0000 0000 0000 0000 0001 左移29位得
0001 0000 0000 0000 0000 0000 0000 0000
TIDYING为2左移29位。即:
0000 0000 0000 0000 0000 0000 0000 0010 左移29位得
0010 0000 0000 0000 0000 0000 0000 0000
TERMINATED为3左移29位。即:
0000 0000 0000 0000 0000 0000 0000 0011 左移29位得
0011 0000 0000 0000 0000 0000 0000 0000

当前运行线程数

int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))
        return;
    c = ctl.get();
}
private static int workerCountOf(int c)  { return c & CAPACITY; }

先来看一下初始c是多少

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int ctlOf(int rs, int wc) { return rs | wc; }

c:
1001 0000 0000 0000 0000 0000 0000 0000 |
++0000 0000 0000 0000 0000 0000 0000 0000++
1001 0000 0000 0000 0000 0000 0000 0000

CAPACITY为1左移29位再减去1。即:
0000 0000 0000 0000 0000 0000 0000 0001 左移29位得
0001 0000 0000 0000 0000 0000 0000 0000 -1得
0000 1111 1111 1111 1111 1111 1111 1111

这个时候就可以算出初始运行线程数
c|CAPACITY
1001 0000 0000 0000 0000 0000 0000 0000 &
++0000 1111 1111 1111 1111 1111 1111 1111++
0000 0000 0000 0000 0000 0000 0000 0000

再来看一下运行状态的计算

private static int runStateOf(int c)     { return c & ~CAPACITY; }

~CAPACITY 为:
1111 0000 0000 0000 0000 0000 0000 0000

~CAPACITY&c
1111 0000 0000 0000 0000 0000 0000 0000 &
++1001 0000 0000 0000 0000 0000 0000 0000++
1001 0000 0000 0000 0000 0000 0000 0000

分析到这里,基本上可以得出一个结论。

线程池用一个原子操作的int类型来存储线程池状态和运行中线程个数两种数据。采用高4(32 ~ 29)位存储线程池状态,低28(28~1)位来存储运行线程数量。

态间的转换

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aIpSbl9f-1655452866389)(https://github.com/wangjinwengithub/-/blob/master/%E7%BA%BF%E7%A8%8B%E6%B1%A0/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%8A%B6%E6%80%81%E8%BD%AC%E6%8D%A2%E5%9B%BE.png?raw=true)]

当一个任务提交时
  1. 会先判断当前运行的线程数是否小于核心线程数,小于会尝试创建一个新的线程直接执行。
  2. 线程数不小于核心线程数或创建新线程失败,会尝试加入到任务队列。
  3. 加入任务队列失败则拒绝任务。
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
    
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        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);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

从源码中可以看到,一个任务提交上来以后,调用的是AbstractExecutorService中的submit方法,将任务包装成future然后调用ThreadPoolExecutor中的execute方法。

submit详细流程图

image

何时销毁

添加任务时创建一个Worker对象,这个Worker对象执行完当前任务会去轮询阻塞队列,如果阻塞队列里面没有任务,就有可能销毁。
对于核心线程来说,可以无限等待任务执行。非核心线程超时没有获取到任务,会进入销毁流程。

如何销毁

添加线程是吧Worker放入一个set集合中,销毁是把相应的Worker对象从set集合中移除。gc自动回收来达到销毁的目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖工-->攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值