赞
踩
在源码解析之前,我们先思考一个问题:为什么要使用线程池?
如果不使用线程池,我们如何在程序中执行一些任务呢?
- java复制代码public static void main(String[] args) {
- doTask1();
- doTask2();
- // ...
- }
- java复制代码public static void main(String[] args) {
- new Thread(Demo::doTask1).start();
- new Thread(Demo::doTask2).start();
- // ...
- }
第一种方式编码简单,也不容易出错,但它的问题是效率不高,无法利用多核处理器的优势。第二种方式能利用多核的优势,为每个任务创建一个线程,由操作系统来调度任务,在任务不多的情况下它能很好的工作,但是当任务数量变多之后,线程创建销毁的性能损耗和线程的资源占用都将成为问题。
线程是宝贵的系统资源。
对于宝贵资源的使用,有一种通用的思想——池化。
这就是为什么我们要使用线程池的原因。下面这段代码是Java线程池ThreadPoolExecutor的基础使用案例。
- java复制代码public static void main(String[] args) {
- ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
- 4, // 核心池大小
- 6, // 最大池大小
- 1, SECONDS, // Worker线程超时时间
- new ArrayBlockingQueue<>(4), // 工作队列
- Thread::new, // 线程工厂
- new ThreadPoolExecutor.AbortPolicy() // 拒绝任务的策略
- );
-
- // 提交task
- threadPool.execute(Demo::doTask1);
- threadPool.execute(Demo::doTask2);
- // ...
- threadPool.shutdown();
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
接下来我们根据上面这段示例代码,深入到源码,分析一下线程池的实现原理。
上面的代码展示了ThreadPoolExecutor最基础的构造方法,一共有6个参数(明明是7个...),构造方法里面都是一些初始化操作,不在赘述,重点关注一下这6个参数。这里我先列举这些核心参数的用途,在后面源码分析的过程中我们会频繁看到这些参数的身影。
ThreadPoolExecutor提交任务的核心方法只有一个,就是execute方法。其他的如submit、invoke方法都是在其父类AbstractExecutorService中使用模板方法模式实现的,归根结底还是调用了execute方法。
所以我们重点关注execute方法提交一个任务后都发生了什么?
- java复制代码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); // 线程池不是RUNNING状态了,执行拒绝策略
- else if (workerCountOf(recheck) == 0)
- // 重新检查,核心池线程可能挂了,添加一个新的,
- // 但是不用设置firstTask(因为task已经添加到workQueue了)
- addWorker(null, false);
- }
- else if (!addWorker(command, false)) // ③ 核心池和工作队列都满了
- reject(command); // 全都满了,或者线程池已经不是RUNNING状态了,执行拒绝策略
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
从上面的源码可以看出,提交一个任务后,主要有3个分支,接下来我们详细分析这3种情况都做了哪些事情。
此时workerCount < corePoolSize,工作队列还是空的。
通过上面execute的源码①的位置,调用了addWorker方法,其源码如下所示。这个方法比较长,我添加了比较详细的注释,后面还有很多地方会遇到这个方法。
- java复制代码private boolean addWorker(Runnable firstTask, boolean core) {
- retry:
- for (;;) {
- int c = ctl.get();
- int rs = runStateOf(c);
- // ① 线程池状态不是RUNNING的时候不能添加新的Worker线程
- /
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。