了解下JUC的线程池学习六(工作线程内部类Worker源码分析)

1.介绍

   线程池中的每一个具体的工作线程被包装为内部类Worker实例,

   Worker继承于AbstractQueuedSynchronizer(AQS),实现了Runnable接口

 

2.实现了Runnable接口

    private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
       /**
        * This class will never be serialized, but we provide a
        * serialVersionUID to suppress a javac warning.
        */
       private static final long serialVersionUID = 6138294804551838833L;

       // 保存ThreadFactory创建的线程实例,如果ThreadFactory创建线程失败则为null
       final Thread thread;
       // 保存传入的Runnable任务实例
       Runnable firstTask;
       // 记录每个线程完成的任务总数
       volatile long completedTasks;
   
       // 唯一的构造函数,传入任务实例firstTask,注意可以为null
       Worker(Runnable firstTask) {
           // 禁止线程中断,直到runWorker()方法执行
           setState(-1); // inhibit interrupts until runWorker
           this.firstTask = firstTask;
           // 通过ThreadFactory创建线程实例,注意一下Worker实例自身作为Runnable用于创建新的线程实例
           this.thread = getThreadFactory().newThread(this);
      }

      // 委托到外部的runWorker()方法,注意runWorker()方法是线程池的方法,而不是Worker的方法
      public void run() {
              runWorker(this);
      }

      // Lock methods
      //
     // The value 0 represents the unlocked state.
     // The value 1 represents the locked state.
     //  是否持有独占锁,state值为1的时候表示持有锁,state值为0的时候表示已经释放锁
     protected boolean isHeldExclusively() {
            return getState() != 0;
     }

     // 独占模式下尝试获取资源,这里没有判断传入的变量,直接CAS判断0更新为1是否成功,成功则设置独占线程为当前线程
      protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                 setExclusiveOwnerThread(Thread.currentThread());
                 return true;
           }
          return false;
      }
   
      // 独占模式下尝试是否资源,这里没有判断传入的变量,直接把state设置为0
      protected boolean tryRelease(int unused) {
             setExclusiveOwnerThread(null);
             setState(0);
             return true;
      }
   
      // 加锁
      public void lock(){

            acquire(1);

      }

      // 尝试加锁
      public boolean tryLock()  {

            return tryAcquire(1);

      }

      // 解锁
      public void unlock()  {

             release(1);

      }

      // 是否锁定
      public boolean isLocked() {

           return isHeldExclusively();

      }
   
      // 启动后进行线程中断,注意这里会判断线程实例的中断标志位是否为false,只有中断标志位为false才会中断
      void interruptIfStarted() {
           Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                 } catch (SecurityException ignore) {
                 }
            }
       }  
   }

   第一点:Worker的构造函数里面的逻辑十分重要,通过ThreadFactory创建的Thread实例同时传入Worker实例,

                因为Worker本身实现了Runnable,所以可以作为任务提交到线程中执行。

                只要Worker持有的线程实例w调用Thread#start()方法就能在合适时机执行Worker#run()。简化一下逻辑如下:

                   // addWorker()方法中构造
                   Worker worker = createWorker();
                    // 通过线程池构造时候传入
                    ThreadFactory threadFactory = getThreadFactory();
                     // Worker构造函数中
                    Thread thread = threadFactory.newThread(worker);
                     // addWorker()方法中启动
                     thread.start();

    第二点:Worker继承自AQS,这里使用了AQS的独占模式,这里有个技巧是构造Worker的时候,

                 把AQS的资源(状态)通过setState(-1)设置为-1,

                  这是因为Worker实例刚创建时AQSstate的默认值为0,此时线程尚未启动,

                  不能在这个时候进行线程中断,见Worker#interruptIfStarted()方法。

                   Worker中两个覆盖AQS的方法tryAcquire()tryRelease()都没有判断外部传入的变量,前者直接CAS(0,1)

                   后者直接setState(0)

   

3.核心方法ThreadPoolExecutor#runWorker()

    final void runWorker(Worker w) {
           // 获取当前线程,实际上和Worker持有的线程实例是相同的
           Thread wt = Thread.currentThread();
           // 获取Worker中持有的初始化时传入的任务对象,这里注意存放在临时变量task中
           Runnable task = w.firstTask;
           // 设置Worker中持有的初始化时传入的任务对象为null
           w.firstTask = null;
           // 由于Worker初始化时AQS中state设置为-1,这里要先做一次解锁把state更新为0,允许线程中断
           w.unlock(); // allow interrupts
           // 记录线程是否因为用户异常终结,默认是true
          boolean completedAbruptly = true;
         try {
             // 初始化任务对象不为null,或者从任务队列获取任务不为空(从任务队列获取到的任务会更新到临时变量task中)
             // getTask()由于使用了阻塞队列,这个while循环如果命中后半段会处于阻塞或者超时阻塞状态,

            //getTask()返回为null会导致线程跳出死循环使线程终结
             while (task != null || (task = getTask()) != null) {
                   // Worker加锁,本质是AQS获取资源并且尝试CAS更新state由0更变为1
                   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
                   // 如果线程池正在停止(也就是由RUNNING或者SHUTDOWN状态向STOP状态变更),

                  //那么要确保当前工作线程是中断状态
                   // 否则,要保证当前线程不是中断状态
                   if ((runStateAtLeast(ctl.get(), STOP) ||
                          (Thread.interrupted() &&
                           runStateAtLeast(ctl.get(), STOP))) &&
                             !wt.isInterrupted())
                                 wt.interrupt();
                       try {
                          // 钩子方法,任务执行前
                           beforeExecute(wt, task);
                           try {
                                    task.run();
                                     // 钩子方法,任务执行后 - 正常情况
                                     afterExecute(task, null);
                                  } catch (Throwable ex) {
                                     // 钩子方法,任务执行后 - 异常情况
                                     afterExecute(task, ex);
                                      throw ex;
                                  }
                             } finally {
                                  // 清空task临时变量,这个很重要,否则while会死循环执行同一个task
                                  task = null;
                                  // 累加Worker完成的任务数
                                  w.completedTasks++;
                                  // Worker解锁,本质是AQS释放资源,设置state为0
                                  w.unlock();
                            }
                      }
                      // 走到这里说明某一次getTask()返回为null,线程正常退出
                      completedAbruptly = false;
                   } finally {
                      // 处理线程退出,completedAbruptly为true说明由于用户异常导致线程非正常退出
                      processWorkerExit(w, completedAbruptly);
                   }

         }

   这里重点拆解分析一下判断当前工作线程中断状态的代码:

   if ((runStateAtLeast(ctl.get(), STOP) ||
        (Thread.interrupted() &&
        runStateAtLeast(ctl.get(), STOP))) &&
             !wt.isInterrupted())
                    wt.interrupt();
              // 先简化一下判断逻辑,如下
              // 判断线程池状态是否至少为STOP,rs >= STOP(1)     
              boolean atLeastStop = runStateAtLeast(ctl.get(), STOP);
              // 判断线程池状态是否至少为STOP,同时判断当前线程的中断状态并且清空当前线程的中断状态
              boolean interruptedAndAtLeastStop = Thread.interrupted() && runStateAtLeast(ctl.get(), STOP);
              if (atLeastStop || interruptedAndAtLeastStop && !wt.isInterrupted()){
                     wt.interrupt();
                }

    注:Thread.interrupted()方法获取线程的中断状态同时会清空该中断状态,

            这里之所以会调用这个方法是因为在执行上面这个if逻辑同时外部有可能调用shutdownNow()方法,

            shutdownNow()方法中也存在中断所有Worker线程的逻辑,

            但是由于shutdownNow()方法中会遍历所有Worker做线程中断,

            有可能无法及时在任务提交到Worker执行之前进行中断,

             所以这个中断逻辑会在Worker内部执行,就是if代码块的逻辑。

             这里还要注意的是:STOP状态下会拒绝所有新提交的任务,不会再执行任务队列中的任务,

             同时会中断所有Worker线程。也就是,即使任务Runnable已经runWorker()中前半段逻辑取出,

             只要还没走到调用其Runnable#run(),都有可能被中断

            假设刚好发生了进入if代码块的逻辑同时外部调用了shutdownNow()方法,

            那么if逻辑内会判断线程中断状态并且重置,那么shutdownNow()方法中调用的interruptWorkers()

            就不会因为中断状态判断出现问题导致二次中断线程(会导致异常)。

 

4.runWorker()方法的核心流程

  第一步:Worker先执行一次解锁操作,用于解除不可中断状态。

  第二步:通过while循环调用getTask()方法从任务队列中获取任务(当然,首轮循环也有可能是外部传入的firstTask任务实例)。

   第三步:如果线程池更变为STOP状态,则需要确保工作线程是中断状态并且进行中断处理,否则要保证工作线程必须不是中断状态。

   第四步:执行任务实例Runnale#run()方法,任务实例执行之前和之后(包括正常执行完毕和异常执行情况)

                 分别会调用钩子方法beforeExecute()afterExecute()

    第五步:while循环跳出意味着runWorker()方法结束和工作线程生命周期结束(Worker#run()生命周期完结),

                  会调用processWorkerExit()处理工作线程退出的后续工作。

 

5.图解

  

 

 

   

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

    

posted @ 2020-09-02 16:52  小窝蜗  阅读(289)  评论(0编辑  收藏  举报