了解下JUC的线程池学习十一(理解可重入锁mainLock成员变量)

 

1.主要的变量

  private final ReentrantLock mainLock = new ReentrantLock();
  private final Condition termination = mainLock.newCondition();

 

2.ThreadPoolExecutor内部成员属性mainLock的引用情况

   

 

 

3.mainLock应用场景

   第一个:tryTerminate

       保证状态TIDYING -> TERMINATED,钩子方法terminated()回调和条件变量唤醒

  第二个:interruptIdleWorkers

       保护工作线程中断的串行化,避免"中断风暴"

  第三个:addWorker

       保护工作线程集合避免并发增加工作线程、保护度量统计数据变更

  第四个:processWorkerExit

       保护度量统计数据变更

   第五个:shutdownshutdownNowawaitTermination

        多次并发调用shutdown()如果不加锁,会反复中断工作线程

   第六个:getPoolSizegetActiveCountgetLargestPoolSizegetTaskCountgetCompletedTaskCount

           保护度量统计数据读取,这些统计数据来一般源于Worker集合的属性统计

 

4.分析下shutdown为何加上mainLock

   public void shutdown() {
      final ReentrantLock mainLock = this.mainLock;
      mainLock.lock();
      try {
           checkShutdownAccess();
           advanceRunState(SHUTDOWN);
           interruptIdleWorkers();
           onShutdown(); // hook for ScheduledThreadPoolExecutor
      } finally {
           mainLock.unlock();
      }
      tryTerminate();
  }

  说明:这里shutdown()中除了tryTerminate(),其他它方法都是包裹在锁里面执行,

           确保工作线程集合稳定性以及关闭权限、确保状态变更串行化,

            中断所有工作线程并且避免工作线程"中断风暴"(多次并发调用shutdown()如果不加锁,会反复中断工作线程)。

 

 public List<Runnable> shutdownNow() {
       List<Runnable> tasks;
      final ReentrantLock mainLock = this.mainLock;
      mainLock.lock();
      try {
           checkShutdownAccess();
           advanceRunState(STOP);
           interruptWorkers();
           tasks = drainQueue();  # <---  多了这一步
      } finally {
          mainLock.unlock();
      }
      tryTerminate();
      return tasks;
  }

 总结: shutdownNow()方法其实加锁的目的和shutdown()差不多,不过多了一步:导出任务队列中的剩余的任务实例列表。 

  awaitTermination()方法中使用到前面提到过的条件变量termination

   // 条件变量必须在锁代码块中执行,和synchronized关键字用法差不多
  public boolean awaitTermination(long timeout, TimeUnit unit)
           throws InterruptedException {
           long nanos = unit.toNanos(timeout);
           final ReentrantLock mainLock = this.mainLock;
           mainLock.lock();
     try {
           // 死循环确保等待执行和状态变更为TERMINATED
           while (runStateLessThan(ctl.get(), TERMINATED)) {
               if (nanos <= 0L)
                     return false;

                     # <-- 确保当前调用线程阻塞等待对应的时间或者线程池状态变更为TERMINATED,再退出等待
                     nanos = termination.awaitNanos(nanos);
             }
             return true;
         } finally {
             mainLock.unlock();
         }
   }

  总结:awaitTermination()方法的核心功能是:确保当前调用awaitTermination()方法

            的线程阻塞等待对应的时间或者线程池状态变更为TERMINATED,再退出等待返回结果,

            这样能够让使用者输入一个可以接受的等待时间进行阻塞等待,

            或者线程池在其他线程中被调用了shutdown()方法状态变更为TERMINATED就能正常解除阻塞。

            awaitTermination()方法的返回值为布尔值,true代表线程池状态变更为TERMINATED或者等待了

            输入时间范围内的时间周期被唤醒,意味则线程池正常退出,结果为false代表等待了超过输入时间

            范围内的时间周期,线程池的状态依然没有更变为TERMINATED

 

注 : 线程池中的工作线程如何优雅地退出,不导致当前任务执行丢失、

          任务状态异常或者任务持有的数据异常,是一个很值得探讨的专题。

 

学习来源:https://www.cnblogs.com/throwable/p/13574306.html

posted @ 2020-09-03 10:39  小窝蜗  阅读(658)  评论(0编辑  收藏  举报