java8线程池源码解析

1.对线程池的理解

1.1 艰辛摸索

看过许多关于线程池的介绍和讲解,看过方腾飞的 《并发编程的艺术》 也看过很多博客关于线程池的讲解,但是总觉得自己理解的不太好,总觉得哪里缺点。后来自己也花时间查看源码,自己去琢磨,多问为什么,结合一些博客的讲解。现在我想把这段时间的成果记录下来,也是为了加深自己的理解吧。
下面所有的源码都是java8 源码。

1.2 线程池的好处

  • 降低性能消耗、提高响应速度:对于应用需要频繁创建线程,而且线程任务都比较简单,比如一些IO任务,线程的生命周期都很短;而线程的创建需要花费一定的CPU时间,所以当任务到来时如果线程已经准备就绪了,而不是重新创建,则会大大提高系统的响应速度。
  • 对线程的集中管理监控:将创建的线程规约在线程池里,则可以对线程的数量和运行状态进行管理并进行监控,可对系统的线程资源进行集中管理。

2.线程池内的一些属性

2.1 线程池参数

看下面的这个线程池的构造器,他有许多参数,这些参数都代表什么意思?

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
  • corePoolSize 核心的线程池数量
  • maximumPoolSize 线程池内最大的线程数量
  • keepAliveTime 线程存活时间
  • unit 时间单位 比如 秒 分钟
  • workQueue 存储任务的阻塞队列
ArrayBlockingQueue 有界阻塞队列,此队列会满。
SynchronousQueue 没有容量的阻塞队列,一次添加必须等待一次获取。反之亦然。
LinkedBlockingQueue 无界阻塞队列 这个阻塞队列用于不会满
  • threadFactory 自定义线程工厂

1.通过自定义线程池 可以给线程池内,线程都赋予更有意义的线程名。
也可以根据喜好做些别的事情,比如记录一下何时创建线程之类的。
2.如果没有则会采用默认的线程池。Executors.defaultThreadFactory()
  • handler 拒绝策略
拒绝策略java 默认提供了四种实现。都是ThreadPoolExecutor的内部类。
1 CallerRunsPolicy :如果当前线程池 处于运行状态 ,直接使用当前线程执行任务,如果是终止状态,则悄悄的直接抛弃。

2. AbortPolicy :对拒绝任务抛弃处理,并且抛出异常。默认采用。
rejectedExecution 内直接抛异常。
3. DiscardPolicy :对拒绝任务直接无声抛弃,没有异常信息。查看源码发现他的rejectedExecution 函数就是一个空实现。

4. DiscardOldestPolicy :对拒绝任务不抛弃,而是抛弃队列里面等待最久的一个任务,然后把拒绝任务加到队列。查看源码
if (!e.isShutdown()) {
   e.getQueue().poll();//获取并移除队列head 
   e.execute(r);//再次execute任务
}

3.Executors 提供的几种线程池

3.1 newFixedThreadPool

  • 固定线程数的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
   return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
   new LinkedBlockingQueue<Runnable>());
}
1.核心 线程数 和 最大线程数一致。
2.采用的阻塞队列是 无界阻塞队列,LinkedBlockingQueue。也就是在极端情况下,阻塞队列 会一直增长,直到堆内存溢出,需要谨慎使用该线程池。

3.2 newCachedThreadPool

  • 缓冲线程池
 public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
    new SynchronousQueue<Runnable>());
}
0.在newCachedThreadPool中如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
1.初看该构造函数时我有这样的疑惑:核心线程池为0,那按照前面所讲的线程池策略新任务来临时无法进入核心线程池,只能进入 SynchronousQueue中进行等待
,而SynchronousQueue的大小为1,那岂不是第一个任务到达时只能等待在队列中,直到第二个任务到达发现无法进入队列才能创建第一个线程? 
2.这个问题的答案在上面讲SynchronousQueue时其实已经给出了,要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。因此即便SynchronousQueue一开始为空且大小为1,第一个任务也无法放入其中,因为没有线程在等待从SynchronousQueue中取走元素。因此第一个任务到达时便会创建一个新线程执行该任务。
3.这个最大线程数是 Integer.MAX 使用中需要注意。

3.3 newWorkStealingPool

jdk 1.8 新加入的。创建一个带并行级别的线程池,并行级别决定了同一时刻最多有多少个线程在执行,如不穿如并行级别参数,将默认为当前系统的CPU个数

3.4 newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。

4. 源码

下面介绍下线程池的源码,由于对写作没有天赋,我就尽我最大努力把源码分析的浅显易懂一些。我介绍下线程池的一些状态属性,然后再从提交一个任务开始跟着源码进行描述一下我对源码的理解。

4.1 线程池状态 和 线程统计

image.png
  • ctl
    先看看线程池中最重要的一个属性 ctl,我觉得是Control的简写,ctl控制着整个线程池的运转。ctl是AtomicInteger类型,线程池利用ctl 的高3位作为记录当前线程池的状态。利用低29位记录线程池中线程数,所以线程池中线程的最大容量为 2^29。
    默认值是 1110 0000 0000 0000 0000 0000 0000 0000 = -536 870 912 ;

  • COUNT_BITS
    COUNT_BITS = Integer.SIZE - 3;
    COUNT_BITS 的意思是 一个整型数 有29位用于统计线程池内线程数;

  • RUNNING 运行状态
    RUNNING 是线程池的初始状态,是一个int 类型的常量,值为 -1 左移 29 位即为 -536 870 912,线程池 的状态
    只有RUNNING 是负数的。

  • SHUTDOWN
    RUNNING --shutDowm()--> SHUTDOWN
    RUNNING状态调用shutDowm()函数进入SHUTDOWN状态。是一个int类型的常量 值为0 ;
    此时线程池不接收新任务,但能处理已添加的任务。

  • STOP
    (RUNNING or SHUTDOWN) ---shutdownNow()--> STOP
    RUNNING状态或者SHUTSOWN状态调用shutDowmNow()函数进入SROP状态。是一个int类型的常量 值为536 870 912 ;
    不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。

  • TIDYING
    是一个int类型的常量 值为1 073 741 824 ;
    SHUTDOWN -> TIDYING:当线程池内线程数为0并且队列内任务数量为0时
    STOP -> TIDYING:当线程池内线程数量为0时

  • TERMINATED
    是一个int类型的常量 值为1 610 612 736 ;
    线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

  • CAPACITY (线程的最大容量)
    1 转移 29位 ,就是 0010 0000 0000 0000 0000 0000 0000 0000,
    然后 减1 ,就变成了0001 1111 1111 1111 1111 1111 1111 1111,刚好就是 低29位 为1 ,就是线程内线程的最大容量 。

4.2 execute 执行任务

线程池添加任务流程图.png

在未来的某个时刻执行给定的任务,这个任务会被一个新线程或者一个已经存在的线程执行。
如果任务不能提交执行,那是因为线程池不处于运行状态或者已经到达容量上限。
那么这个任务就会被RejectedExecutionHandler处理。
execute()代码逻辑:

1.当通过excute(Runable) 提交一个任务时,如果当前线程池内线程数量小于corePoolSize 数量,即使线程池内线程处于闲置状态,线程池也会创建一个新的线程。
2.如果线程内线程数大于corePoolSize 数量小于 maximumPoolSize 数量,则会将任务 提交到 workQueue(阻塞队列)。
3.如果workQueued队列已经满了,而当前线程池内线程数量小于maximumPoolSize 则创建一个新的线程执行。
4.如果此时线程池内线程数量 不小于maximumPoolSize 数量,则执行拒绝策略。
    /**
     *
     * @param command the task to execute
     * @throws RejectedExecutionException at discretion of
     *         {@code RejectedExecutionHandler}, if the task
     *         cannot be accepted for execution
     * @throws NullPointerException if {@code command} is null
     */
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
       
        
        int c = ctl.get();
        //1.如果正在运行的线程数 小于 corePoolSize,创建一个线程执行command.
        
        if (workerCountOf(c) < corePoolSize) {
        //创建一个线程执行command
            if (addWorker(command, true))
                return;
                //如果失败 获取ctl recheck runstate
            c = ctl.get();
        }
        //2.如果是运行状态 并且追加command 到队列成功
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //重新检查运行状态 不是运行中状态 则 从队列移除 command,让RejectedExecutionHandler 处理command。
            if (! isRunning(recheck) && remove(command))
            
                reject(command);
            else if (workerCountOf(recheck) == 0)
            //如果当前运行中的线程数是0 ,则创建一个新线程。
                addWorker(null, false);
        }
        //3.如果添加任务到队列失败,则新建一个线程执行任务。
        else if (!addWorker(command, false))
        //如果新建线程失败,让RejectedExecutionHandler 处理command。
            reject(command);
    }

4.3 addWorker

检查是否可以根据当前线程池状态和绑定条件(核心线程或者最大线程)添加一个新的工作线程。
如果可以的话那么调整运行的线程数量。
并且 如果线程成功创建和运行的话,那么firstTask将是新线程的第一个任务被执行。
如果线程池已经关闭或者正在关闭,函数将返回false.
如果线程创建失败,可能的原因是因为线程工厂返回null,或者异常(最可能的异常就是在执行Thread,start()时产生OutOfMemoryErro )。
接着回滚以上操作。
参数:core:如果为true 增加核心线程,false 增加最大线程。

private boolean addWorker(Runnable firstTask, boolean core) {
       retry://外层循环 循环检查运行状态
       for (;;) {//
           int c = ctl.get();//获取 ctl 值
           int rs = runStateOf(c);//获取当前线程池状态

           // Check if queue empty only if necessary.
           //如果有必要的话 检查任务阻塞队列是否为空
           
           //这里第一个条件检查 运行状态 是否为RUNNING 状态。因为只有RUNNING状态不满足 rs>= SHUTDOWN 条件。
           //第二个条件 咋一看 不好懂,有点晕。
           //仔细分析下,当运行状态为SHUTDOWN 状态时,线程池会执行完阻塞队列内的任务。但是不再接收新任务。
           //也就是说 如果 是shutdown 状态,firstTask is null ,阻塞队列里面还有任务时,是允许创建新的新工作线程的。
           if (rs >= SHUTDOWN &&
               ! (rs == SHUTDOWN &&
                  firstTask == null &&
                  ! workQueue.isEmpty()))
               return false;

        //内循环 调整工作线程数
           for (;;) {
           //获取当前工作线程数
               int wc = workerCountOf(c);
               //如果 大于 最大容量 capacity, 或者 根据绑定条件 大于 核心线程 或者 最大线程数。return false。
               
               
               if (wc >= CAPACITY ||
                   wc >= (core ? corePoolSize : maximumPoolSize))
                   return false;
               //cas 调整 工作线程数
               if (compareAndIncrementWorkerCount(c))
                   break retry;//如果成功 跳出外层循环
               c = ctl.get();  // Re-read ctl
               //cas 调整 工作线程数失败 
               //检查 线程状态 是否改变
               //如果没有改变 继续执行 内循环
               //如果状态 改变 执行外循环。
               if (runStateOf(c) != rs)
                   continue retry;
               // else CAS failed due to workerCount change; retry inner loop
           }
       }
    //开始添加 worker
       boolean workerStarted = false;
       boolean workerAdded = false;
       Worker w = null;
       try {
       //创建一个 工作线程
       //worker 构造器内部 调用 线程工厂 创建一个线程。
       
           w = new Worker(firstTask);
           final Thread t = w.thread;
           if (t != null) {
               final ReentrantLock mainLock = this.mainLock;
               //同步锁 阻塞的获取锁
               mainLock.lock();
               try {
                   // Recheck while holding lock.
                   // Back out on ThreadFactory failure or if
                   // shut down before lock acquired.
                   //获取池运行状态
                   int rs = runStateOf(ctl.get());
             //rs 即为当前线程池运行状态,如果状态是RUNNING 或者 
             //第一个条件 是RUNNING 状态 
             //第二个条件 是SHUTDOWN状态时 firstTask 必须是 null ,因为 shutdown 状态 不能提交新的任务。但可以创建新工作线程。
             
                   if (rs < SHUTDOWN ||
                       (rs == SHUTDOWN && firstTask == null)) {
                       //t.isAlive() 如果线程已经start 则返回true ,所以这里是判断 线程t 是不是还可以执行start()。
                       
                       if (t.isAlive()) // precheck that t is startable
                       // 
                           throw new IllegalThreadStateException();
                       //工作线程 添加到 集合workers 中
                       workers.add(w);
                       //修改 poolSize
                       int s = workers.size();
                       if (s > largestPoolSize)
                           largestPoolSize = s;
                       workerAdded = true;
                   }
               } finally {
               //释放 同步锁
                   mainLock.unlock();
               }
               
               if (workerAdded) {
               //如果添加成功 启动线程
                   t.start();
                   workerStarted = true;
               }
           }
       } finally {
       //如果线程启动失败 回滚操作
           if (! workerStarted)
               addWorkerFailed(w);w
               //线程安全的 从工作线程集合中移除 如果w存在的话,就CAS 调整 工作线程数。并且尝试停止线程池
       }
       return workerStarted;
       //返回启动成功 与否
   }

4.3 addWorkerFailed

执行addWorker添加任务失败之后 调用该方法执行回滚操作。
回滚操作主要做了以下三件事情:

  1. 从workers 工作线程集合中移除 worker 
  2. worker Count  减 1
  3. 尝试终止线程池,如果满足终止条件的话 就会终止线程池。
/**
     * Rolls back the worker thread creation.
     * - removes worker from workers, if present
     * - decrements worker count
     * - rechecks for termination, in case the existence of this
     *   worker was holding up termination
     */
    private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();//获取主锁
        try {
            if (w != null)
            //从workers 移除指定worker
                workers.remove(w);
                //workerCount 减 1
            decrementWorkerCount();
            //尝试终止线程池 终止的条件比较苛刻
            tryTerminate();
        } finally {
            mainLock.unlock();//释放锁
        }
    }

4.4 runWorker 执行任务

添加worker 成功之后,worker就开始执行runWorker()了。
这个方法是比较重要的方法,是线程池的核心。是worker 线程 运行循环,重复地从队列中获取任务并执行它们,同时处理以下一些问题:

  1.从worker 的firstTask 开始执行,随着线程的运行,worker不停的执行getTask() 获取任务,如果拿不到任务,就会根据线程的池配置信息 来决定是否让 worker退出。如果worker执行过程中抛出异常,会导致worker退出,并试图创建一个新的线程替代当前worker。
    2.执行任务之前,获取锁确保任务执行不被其他线程干扰。如果不是STOP状态,worker线程需要清除中断标记。
    3.处理每个任务之前都要执行钩子函数 beforeExecute(),如果钩子函数抛出异常那么也会出发worker 过早的退出,并尝试创建一个新的worker 替代它。
    4.假如执行beforeExecute()顺利,接下来就是 执行任务了。如果执行中出现异常,那么就会捕获这些异常,进行包装成 runtimeException 和 error,Throwable也被包装成 Error的因为Ruunabl.run 不能抛出Throwabl 异常。最后把这些异常 和 worker 发给 afterExecute()处理。
    5.任务执行完成后,执行afterExecute(),也可能会产生异常,任何的异常都会导致worker 销毁。
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;//获取firstTask
        w.firstTask = null;//清除 firstTask
        w.unlock(); 
        // 允许worker 中断,为什么unlock()就可以允许中断?因为在worker构建实列时。worker的state 属性被设置为-1,在调用池shutDownNow()函数分别对worker进行终止时,会判断worker 的 state >= 0 ,如果不符合 就不会终止。
        boolean completedAbruptly = true;//是否突然完成,就是如果执行任务时,意外退出,该标记就不会被修改为false.
        try {
        1.先执行woker的firstTask 如果firstTask 已经执行,从任务阻塞队列获取任务。
        2.直到获取不到任务跳出while循环。
            while (task != null || (task = getTask()) != null) {
                w.lock();//获取锁
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                1.如果 小于 STOP 状态 ,就进行 中断标记的清除,
                2.Thread.interrupted() 会清除当前线程的 中断标记。
                3.清除中断标记是为了消除 外部代码 对worker 的中断操作。
                4.如果大于等于 STOP状态 就会中断线程。
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                //执行任务前 处理
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                    //执行任务
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                    //任务执行后处理操作
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;//执行任务数++;
                    w.unlock();//施放锁。
                }
            }
            completedAbruptly = false;//不是意外退出。
        } finally {
        //处理worker 退出。
            processWorkerExit(w, completedAbruptly);
        }
    }

4.5 getTask() 获取任务

阻塞或者限时的通过getTask() 函数获取任务。不仅仅是获取任务,同时通过是不是返回null控制者worker 线程的退出与否。以下情况会导致函数返回null,导致worker 退出:

    1.当前池执行的线程数超过了maximumPoolSize设置。
    2.线程池stop状态
    3.线程池时shutdown 状态 并且 任务阻塞队列 是空的。
    4.当前worker 获取任务时等待超时。具体的需要根据是否允许核心线程退出,是否大于核心线程,设置判断
     private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
          //timedOut  是否已经超时的标记,如果超时获取任务 ,timedOut 设置为 true,就会影响到后面的逻辑判断。
        for (;;) {//循环的判断线程池状态 
            int c = ctl.get();//获取ctl 值
            int rs = runStateOf(c);//获取线程池运行状态

            // Check if queue empty only if necessary.
          1.如果是大于等于 shutdown 状态 ,并且 是大于等于stop状态 那么获取任务失败,线程退出
          2.如果是shutdown 状态,并且 任务阻塞队列 workQuenu is empty 那么获取任务失败,线程退出。
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();//调整工作线程数减1 ;
                return null;//返回 task is null
            }

          //获取池正在运行工作线程数
            int wc = workerCountOf(c);

            // Are workers subject to culling?
           //是否 执行定时的获取任务标记
          // 如果 运行核心线程退出 或者 正在运行线程数大于核心线程 ,执行定时的获取任务。
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

          //判断当前worker 是否满足 退出条件,如果满足就执行cas 调整线程数 减 1 。并返回null
          
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                //三元表达式
                //wordQuenu.poll 是等待keepAliveTime 时间 获取任务,如果超时返回null
                //workQuenu.take 阻塞的获取任务,直到获取到一个任务为止。
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;//如果 获取到的 r is null,那么说明 执行了 poll 获取任务超时。
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

4.6 processWorkerExit 处理worker退出

  1. 对即将死亡worker 进行清理 和 记账(统计worker执行的任务数)
  2. 如果worker突然死亡(执行中有异常) 就需要调整运行的线程数
  3. 将worker 从worker线程集合中移除。(除名)
  4. 可能会终止线程池
  5. 在小于STOP状态前提下,如果由于执行 用户任务异常导致线程退出,创建新线程替代
  6. 在小于STOP状态前提下,不允许核心线程退出,如果运行中的线程小于核心线程,创建新线程替代。
 private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // 如果runWorker中出现异常,那么调整woker 线程数量
            decrementWorkerCount();//调整数量,循环cas 保证调整成功

        final ReentrantLock mainLock = this.mainLock;//
        mainLock.lock();//获取锁
        try {
            completedTaskCount += w.completedTasks;//给即将死亡的工人记录
            workers.remove(w);//花名册除名
        } finally {
            mainLock.unlock();//释放锁
        }

        tryTerminate();//尝试终止池

        int c = ctl.get();
     //寻找新的worker 替代死亡的worker
        if (runStateLessThan(c, STOP)) {//如果 池STOP 那么就没必要找新的worker啦
            if (!completedAbruptly) {//如果 正常执行 终止
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;//判断需要保留的最小worker数量
                if (min == 0 && ! workQueue.isEmpty())//如果是 0 并且 任务 队列 不为 0 
                    min = 1;//至少保留一个 把活干完
                if (workerCountOf(c) >= min) //如果 当前worker 数量 大于 至少 保留数量 
                    return; // 不需要 寻找替代者
            }
            addWorker(null, false);// 找新的worker 替代 firstTask is null
        }
    }

4.7 tryTerminate 尝试终止池

1.如果池是shutdown 并且 阻塞队列 为空 并且 工作线程数 为空 转换为终止状态

2.如果 池是 stop 并且 工作线程为空 转换为 终止状态

3.满足终止条件,但是工作线程不为 0 ,终止一个空闲线程

 final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
                1.如果池处于 允许状态,不终止池。
                2.如果池状态 >= TIDYING,说明池正在终止,不需要再次终止
                3.如果 池状态 == shutdown 并且 workQuenu 不为空,需要执行完剩下的任务,不终止。
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            //如果池可以被终止  但是 工作线程 不为 0 ,即使 都池状态 和 队列 都满足终止条件 
            //但是 工作线程数 还存在 那么就要终止一个空闲线程
            //为什么终止一个空闲线程?
            //因为此时任务队列为空, 有worker 在getTask 时 从阻塞队列中 take  任务 ,
            //take 方法会一直阻塞worker 直到有任务,但是现在池不再接受新的任务,所以worker 会被一直阻塞
            //由于take通过LockSupport.park()阻塞线程,而LockSupport.park()能响应中断信号
            //所以通过终止 被阻塞的 worker 使其抛出 InterruptedException
            //抛出异常之后 worker最终会被处理退出 ,就会调用该函数,又会终止下一个被take阻塞的worker
            //这样所有被take 阻塞的线程 会 一个一个的 被终止。
            //这一点线程池的设计者 真是很厉害啊 ,想了好久 才发现这一点。
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);
                return;
            }

            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();//获取锁
            try {
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {//设置pool 为 TIDYING ,线程数为0 
                    try {
                        terminated();//转换为终止状态
                    } finally {
                        ctl.set(ctlOf(TERMINATED, 0));//设置pool 为shutdown ,线程数为0
                        termination.signalAll();//唤醒所有等待在终止condition上的线程
                    }
                    return;
                }
            } finally {
                mainLock.unlock();//施放锁
            }
            // else retry on failed CAS cas 修改 ctl 失败 ,重试。
        }
    }

4.8 interruptIdleWorkers

中断可能等待任务的线程。如果队列任务是空的,有的线程会被一直阻塞,调用该方法会中断那些被一直阻塞的线程:

 private void interruptIdleWorkers(boolean onlyOne) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers) {
                Thread t = w.thread;
                if (!t.isInterrupted() && w.tryLock()) {
                    try {
                        t.interrupt();
                    } catch (SecurityException ignore) {
                    } finally {
                        w.unlock();
                    }
                }
                if (onlyOne)
                    break;
            }
        } finally {
            mainLock.unlock();
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容