深度解析「线程池源码」-18

137 阅读2分钟

这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战

继续线程池源码系列

shutdown方法

image.png

  1. 检查权限
  2. 设置当前线程池的状态是SHUTDOWN,如果已经是SHUTDOWN那么就直接返回
  3. 设置中断标志
  4. 尝试将状态改为Terminate
  • 其中3的操作如下
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();
    }
}
  • 以上代码是设置中断标志操作,在整个函数全局加锁
  • 只创建了一个线程t去调用interrupt设置中断标志
  • 紧接着尝试获取Worker自己的锁,获取成功则设置中断标志
  • 如果已经获取了锁,那么就不会被中断 来看方法5

image.png

  • 经过上面特判之后
  1. 开始尝试用CAS将当前的线程设置为TIDYING状态
  2. 如果要是cas成功,则调用终止函数
  3. 将当前的线程池状态设为TERMINATED
  4. 将所有的await的方法阻塞(使用的Condition)

shutdownNow操作

image.png

  1. 进行权限检查
  2. 将线程池状态设为STOP
  3. 中断所有线程
  4. 将队列任务移动到tasks
  • 其主要做了,就是当执行shutdownNow方法后
  • 线程池就不会再接受新的任务了,而且会丢弃工作队列里面的任务
  • 正在执行的任务也会被中断,并且不会等待激活的任务执行完成

awaitTermination

image.png

  1. 检验线程池的状态是否为TERMINATED,全局采用了 独占锁
  • 当线程调用了TERMINATED方法后,当前线程就会被阻塞,然后死循环的方式去判断状态
  • 代码中的nanos是设置的超时时间,用来检验是否小于0,要是小于0就不需要等待直接返回
  • 要是nanos大于0,就调用awaitNanos进行赋值

总结

  • 其实我们去查看线程池源码的时候,几乎每个方法里面都会有ctl这个变量
  • 这也是线程池之所以设计精妙的原因,只用一个原子变量来记录线程池的状态和线程池中的个数
  • 进而通过线程池的状态来控制任务的执行
  • 其中每个Worker线程还可以处理多个任务
  • 池化的思想减少了线程创建与销毁带来的开销