上一篇文章讲了无关线程池的一些简略的用法,这篇文章次要是从源码的角度进一步带大家理解线程池的工作流程和工作原理。

首先先来回顾下如何应用线程池开启线程

private static void createThreadByThreadPoolExecutor() {    ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());    for (int i = 0; i < 10; i++) {        MyThread myThread = new MyThread();        executor.execute(myThread);    }

能够看到其实没有其它非凡的中央,除了构建线程池的代码,其它最终要的就是executor.execute(myThread) 行代码了。

筹备工作

在多线程系列的第一篇文章中提到了线程和过程的状态,线程池同样也有状态,如下:

  • Running: 容许承受新的工作,并且解决队列中的工作
  • Shutdown: 不承受新的工作,然而依然会解决队列中的工作
  • Stop: 不承受新的工作,不解决队列中的工作,而且中端在运行的工作
  • Tidying: 所有的工作都曾经终端,并且工作线程数归0,该状态下的线程都会调用terminated() 函数
  • Terminated:terminated() 函数调用完后就进入了此状态

首先须要晓得4个概念WorkerworkersworkQueuetask.

  • 在线程池中有个比拟重要的类,那就是Worker ,能够看到其实现了Runnable接口(其实就是工作线程),继承了AbstractQueuedSynchronizer类(俗称AQS,在多线程中是很重要的类)

  • workers 就是Worker 的一个汇合,private final HashSet<Worker> workers = new HashSet<Worker>();
  • task :须要执行的工作,也就是execute() 中的参数,实现了Runnable接口
  • workQueue 就是工作队列,就是上一篇文章中线程池构造函数中的工作队列,外面存储的就是须要执行的工作,队列是实现BlockingQueue接口的类,有以下这些实现

execute()

本办法传进去的类是须要实现Runnable接口的,作为一个command 传进去

遇到新的工作

  1. 如果工作线程数 < 外围线程数,那么间接加1个worker
  2. 如果线程池是失常的工作状态,并且工作队列可能增加工作,此时须要第二轮判断

    1. 如果线程池因为某种原因不失常了,并且可能胜利从工作队列中删除工作,那么间接采取回绝策略
    2. 如果此时工作线程数为0,此时须要新建一个线程(并且这里创立的是非核心线程)来执行这个工作,为什么是null呢,因为曾经把工作放在工作队列外面了。如果新建的worker的firstTask是该工作的话,就会反复执行两次工作。
    3. 其它状况也就是失常状况下,是啥都不干,因为次要目标是把工作放到工作队列中
  3. 如果工作队列曾经满了,则须要判断是否可能胜利增加一个非核心线程,如果连非核心线程数都满足不了了,就是线程池真的搞不了了,累了,所以也会调用回绝策略。
留神:外围线程和非核心线程只是语义上的说法,没有实质上的区别

addworker()

addworker 的作用是查看是否能够依据以后池状态和给定界线(外围或最大值)增加新线程 ,并且通过第二个参数来断定是否创立外围线程,当为true的时候就是外围线程,反之就是非核心线程。源码里的正文如下

来看看代码具体是如何的

  1. 一进来就是一个死循环,这个死循环最次要的目标是确认线程池状态是否失常。如果线程池的状态大于SHUTDOWN,也就是处于STOP、TIDYING或者TERMINATED的时候,线程池都没了,还创立worker干啥,间接返回fasle;当线程池处于SHUTDOWN的时候,又得再次判断:

    1. 传进来的工作如果不为空,那么就不必增加worker的,银行上班了,办理完当初的顾客就不在办理了,不然始终来那不是始终不能上班。
    2. 传进来的工作为空,然而工作队列为空,同样不必增加worker,银行都没有工作了,而且又到点上班了。除了以上3中状况返回false,其它状况不做任何反馈,往下走。
  2. 又是一个死循环,首先失去工作线程数如果超过了边界,比方超过了容量、外围线程数或者最大线程数,就不必增加worker了,银行切实是办理不了新的顾客了;当工作线程数失常的状况下,通过CAS来减少工作线程数,如果能减少胜利就退出最外层循环。如果减少工作线程失败,那就是其它线程减少了该数量,如果此时线程池的运行状态产生了扭转,则反复外层循环,否则就自旋直到胜利减少工作线程数。
  3. 到这里就能够说根本排除了阻碍,以传进来的firstTask新建一个Worker,而后获取Worker里的线程。如果线程为null,那么就间接执行增加Worker失败的逻辑,否则就是失常的逻辑。
  4. 先来看失常的逻辑,拿到锁,并且开始又一次的获取线程池的状态

    1. 如果线程是失常运行状态,或者说是敞开状态下firstTask依然为null,此时如果线程是alive 那么而阐明线程曾经开启,间接抛出异样。
    2. 失常状况下是wokers汇合中增加新的worker元素,并且调整线程池最大值,设置workerAdded标记为true。
    3. 重点 ,当workerAdded为true的时候,开启工作线程,也就是代码中的t.start()
  5. 再来看失败的逻辑 ,是在第3点结构一个Worker对象,获取线程的时候,有可能获取到的线程为空,这样其实就是减少worker失败,返回false,在返回false之前会触发执行失败的逻辑addWorkerFailed() 逻辑。整个的逻辑是比较简单的,就不再破费篇幅去论述。

到这里,整个addWorker()的流程是比拟清晰的,值得一提的就是第2行代码中的retry 这个看起来是关键字,但其实不是,仅仅只是一个相似标记位的货色,能够是retry,也能够是abc。

通常是配合for循环来应用,搭配continue和break能够达到goto的成果。比方continue retry; 就是跳到一开始最外层的for循环,break retry; 相当于退出整个循环。写一个最简略的例子大家都能晓得了。

public class RetryExample {    public static void main(String[] args) {        abc:        for (int i = 0; i < 10; i++) {            for (int j = 0; j < 10; j++) {                if (j == 2) {                    continue abc;                }                if (i == 8) {                    break abc;                }                System.out.println("i: " + i + ", j: " + j);            }        }    }}

输入后果如下图所示

创作不易,如果对你有帮忙,欢送点赞,珍藏和分享啦!

上面是集体公众号,有趣味的能够关注一下,说不定就是你的宝藏公众号哦,根本2,3天1更技术文章!!!