• ThreadPoolExecutor 线程池原理分析


    1.介绍

    1.1 整体结构

    1.2 相关接口,类,方法

    Executor 顶层接口

    一个运行新任务的简单接口,只声明了一个方法:  

    void execute(Runnable command);

    返回值为 void,参数为Runnable 类型,用来执行传进去的任务的;

    ExecutorService 接口

    继承了 Executor 接口,声明一些方法:submit、invokeAll、invokeAny 以及shutDown 等

    AbstractExecutorService抽象类

    实现了 ExecutorService 接口,基本实现了 ExecutorService 中声明的所有方法;

    ThreadPoolExecutor 类

    继承了类 AbstractExecutorService。

    1.3 ThreadPoolExecutor核心属性和方法

    execute() 方法: Executor 中声明的方法,ThreadPoolExecutor 具体的实现,是核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行

    submit( )方法: ExecutorService 中声明的方法,在 AbstractExecutorService 具体的实现,在ThreadPoolExecutor 中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和 execute() 方法不同,它能够返回任务执行的结果, submit() 实际上还是调用的 execute() 方法,只不过它利用了Future 来获取任务执行结果。

    shutdown()和 shutdownNow():是用来关闭线程池的。

    内部类Worker: 线程池的工作线程类

    workers属性:一个Set集合,储存线程池的所有工作线程Worker;

    addWorker方法:加入到当前线程池,开启线程并执行

    runWorker:执行task;

    processWorkerExit:清理线程池中的线程

    还有很多其他的方法:getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount() 等获取与线程池相关属性的方法,

    2. ThreadPoolExecutor分析

    2.1 线程池状态

    线程池一共有五种状态, 各状态值所代表的的含义和该状态值下可执行的操作,如下:

    RUNNING

    接收新任务,并且也能处理阻塞队列中的任务。

    SHUTDOWN

    不接收新任务,但是却可以继续处理阻塞队列中的任务。

    STOP

    不接收新任务,同时也不处理队列任务,并且中断正在进行的任务

    TIDYING

    所有任务都已终止,workercount(有效线程数)为0,线程转向 TIDYING 状态将会运行 terminated() 钩子方法。

    TERMINATED

    terminated() 方法调用完成后变成此状态。

    生命周期状态流转如下图所示:

    进入TERMINATED的条件如下:

    1. 线程池不是RUNNING状态;
    2. 线程池状态不是TIDYING状态或TERMINATED状态;
    3. 如果线程池状态是SHUTDOWN并且workerQueue为空;
    4. workerCount为0;
    5. 设置TIDYING状态成功。

    2.2 ctl

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

    private static final int COUNT_BITS = Integer.SIZE - 3; //31-3=29位

    private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;

    // runState 状态通过int表示.,所有状态左移29位,

    private static final int RUNNING = -1 << COUNT_BITS;

    private static final int SHUTDOWN = 0 << COUNT_BITS;

    private static final int STOP = 1 << COUNT_BITS;

    private static final int TIDYING = 2 << COUNT_BITS;

    private static final int TERMINATED = 3 << COUNT_BITS;

    // Packing and unpacking ctl

    private static int runStateOf(int c) { return c & ~COUNT_MASK; } //获取运行状态;

    private static int workerCountOf(int c) { return c & COUNT_MASK; } //获取活动线程数

    private static int ctlOf(int rs, int wc) { returnrs | wc; } //获取运行状态和活动线程数的值。

    线程池内部使用一个变量维护两个值:运行状态(runState)和线程数量 (workerCount) 将十进制 int 值转换为二进制的值,共32位,高3位代表运行状态(runState ),低29位代表工作线程数(workerCount)ctlOf方法,将这两个值进行位或运算。

    2.3 构造方法

    public ThreadPoolExecutor(int corePoolSize,

                int maximumPoolSize,

                long keepAliveTime,

                TimeUnit unit,

                BlockingQueue<Runnable> workQueue,

                ThreadFactory threadFactory,

                RejectedExecutionHandler handler) ;

    其他几个方法都是调用到这个构造方法 ;

    • corePoolSize:

    为线程池的核心线程基本大小。

    当线程池中的线程数目达到 corePoolSize 后,就会把到达的任务放到缓存队列当中;

    • maximumPoolSize

    为线程池最大线程大小。

    • keepAliveTime 和 unit 

    则是线程空闲后的存活时间。线程池维护线程所允许的空闲时间。当线程池中的线程数量 > corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime;

    • workQueue:

    用于存放任务的阻塞队列。等待队列,当任务提交时,如果线程池中的线程数量大于等于corePoolSize的时候,把该任务封装成一个Worker对象放入等待队列;

    一般来说,这里的阻塞队列有以下几种选择:

    直接切换这种方式常用的队列是SynchronousQueue;

    使用无界队列:一般使用基于链表的阻塞队列LinkedBlockingQueue。如果使用这种方式,那么线程池中能够创建的最大线程数就是corePoolSize,而maximumPoolSize就不会起作用了。当线程池中所有的核心线程都是RUNNING状态时,这时一个新的任务提交就会放入等待队列中。

    使用有界队列一般使用ArrayBlockingQueue。使用该方式可以将线程池的最大线程数量限制为maximumPoolSize,这样能够降低资源的消耗,但同时这种方式也使得线程池对线程的调度变得更困难,因为线程池和队列的容量都是有限的值,所以要想使线程池处理任务的吞吐率达到一个相对合理的范围,又想使线程调度相对简单,并且还要尽可能的降低线程池对资源的消耗,就需要合理的设置这两个数量。

    • threadFactory

    用来创建新线程。默认使用Executors.defaultThreadFactory() 来创建线程。使用默认的ThreadFactory来创建线程时,会使新创建的线程具有相同的NORM_PRIORITY优先级并且是非守护线程,同时也设置了线程的名称。

    • handler:(拒绝策略)

    当队列和最大线程池都满了之后的饱和策略。RejectedExecutionHandler类型的变量,表示线程池的饱和策略。如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务。线程池提供了4种策略:

    AbortPolicy:直接抛出异常,这是默认策略;

    CallerRunsPolicy:用调用者所在的线程来执行任务;

    DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;

    DiscardPolicy:直接丢弃任务

    2.4 Worker类

    2.4.1 简单说明

    Worker 是 ThreadPoolExecutor的一个内部类,主要是用来维护线程执行任务的中断控制状态,它实现了Runnable 接口同时继承了AQS,实现 Runnable 接口意味着 Worker 就是一个线程,继承 AQS 是为了实现独占锁这个功能。线程池中的每一个线程被封装成一个Worker对象,ThreadPool维护的其实就是一组Worker对象;

    2.4.2 Worker类定义

    private final class Worker

      extends AbstractQueuedSynchronizer

      implements Runnable

    {

      private static final long serialVersionUID = 6138294804551838833L;

      final Thread thread; // 处理任务的线程。

      Runnable firstTask; //用来保存传入的任务

      /** Per-thread task counter */

      volatile long completedTasks;

      //构造函数,初始化AQS的state值为-1

      Worker(Runnable firstTask) {

        setState(-1); // AQS中默认的state是0,如果刚创建了一个Worker对象,还没有执行任务时,这时就不应该被中断, 将state设置为-1是为了禁止在执行任务前对线程进行中断。

        this.firstTask = firstTask;

        this.thread = getThreadFactory().newThread(this);

      }

      /** Delegates main run loop to outer runWorker. */

      public void run() {

        runWorker(this);

      }

      //state 值0表示未锁定状态。1表示锁定

      protected boolean isHeldExclusively() {

        return getState() != 0;

      }

      // 不允许重入

      protected boolean tryAcquire(int unused) {

        if (compareAndSetState(0, 1)) {

          setExclusiveOwnerThread(Thread.currentThread());

          return true;

        }

        return false;

      }

      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(); }

      void interruptIfStarted() {

        Thread t;

        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {

          try {

            t.interrupt();

          } catch (SecurityException ignore) {

          }

        }

      }

    }

    2.4.3 runWorker方法

    final void runWorker(Worker w) {

      Thread wt = Thread.currentThread();

      Runnable task = w.firstTask; // 获取第一个任务

      w.firstTask = null;

      w.unlock(); // 允许中断

      boolean completedAbruptly = true; // 是否因为异常退出循环

      try { // 如果task为空,则通过getTask来获取任务

        while (task != null || (task = getTask()) != null) {

          w.lock();

          // 如果线程池正在停止,那么要保证当前线程是中断状态;

          如果不是的话,则要保证当前线程不是中断状态; 这里要考虑在执行该if语句期间可能也执行了shutdownNow方法,shutdownNow方法会把状态设置为STOP,

          // 使用Thread.interrupted()来判断是否中断 ,是为了确保在RUNNING或者SHUTDOWN状态时线程是非中断状态的,因为Thread.interrupted()方法会复位中断的状态。

          if ((runStateAtLeast(ctl.get(), STOP) ||

            (Thread.interrupted() &&

            runStateAtLeast(ctl.get(), STOP))) &&

            !wt.isInterrupted())

                       wt.interrupt();

          try {

            beforeExecute(wt, task); //ThreadPoolExecutor类中空方法,留给子类来实现

            try {

              task.run();

              afterExecute(task, null); //ThreadPoolExecutor类中空方法,留给子类来实现

            } catch (Throwable ex) {

              afterExecute(task, ex);

              throw ex;

            }

          } finally {

              task = null;

              w.completedTasks++;

              w.unlock();

          }

        }

      completedAbruptly = false;

      } finally {

        processWorkerExit(w, completedAbruptly);

      }

    }

    总结一下执行过程:

    1. while循环不断地通过getTask()方法获取任务;

    2. getTask()方法从阻塞队列中取任务;

    3. 如果线程池正在停止,那么要保证当前线程是中断状态,否则要保证当前线程不是中断状态;

    4. 调用 task.run()执行任务;

    5. 如果task为null则跳出循环,执行processWorkerExit()方法;

    6. runWorker方法执行完毕,也代表着Worker中的run方法执行完毕,销毁线程。

    2.4.4 getTask方法

    private Runnable getTask() {

      boolean timedOut = false; // 表示上次从阻塞队列中取任务时是否超时( poll() 方法)

      for (;;) {

        int c = ctl.get();

        // 如果有必要检查队列为空

        / * 如果线程池状态rs >= SHUTDOWN,也就是非RUNNING状态,再进行以下判断:

        * 1. rs >= STOP,线程池是否正在stop;

        * 2. 阻塞队列是否为空。

        * 如果以上条件满足,则将workerCount减1并返回null。

        * 如果当前线程池状态的值是SHUTDOWN或以上时,不允许再向阻塞队列中添加任务。

        */

        if (runStateAtLeast(c, SHUTDOWN)

          && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {

          decrementWorkerCount();

          return null;

        }

        int wc = workerCountOf(c);

           // timed变量用于判断是否需要进行超时控制。

        // allowCoreThreadTimeOut默认是false,也就是核心线程不允许进行超时;

        // wc > corePoolSize,表示当前线程池中的线程数量大于核心线程数量;

        // 对于超过核心线程数量的这些线程,需要进行超时控制

        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        /* wc > maximumPoolSize,可能是因为同时执行了setMaximumPoolSize方法;

      timed && timedOut =true,表示当前操作 超时控制+上次从阻塞队列中获取任务发生了超时;

      如果有效线程数量大于1,或者阻塞队列是空的,将workerCount减1;

        如果减1失败,则返回重试。

      如果wc == 1时,也就说明当前线程是线程池中唯一的一个线程了。 */

        if ((wc > maximumPoolSize || (timed && timedOut))

          && (wc > 1 || workQueue.isEmpty())) {

            if (compareAndDecrementWorkerCount(c))

              return null;

        continue; // 如果减1失败,则返回重试

        }

      //如果没有上面的情况则conf队列中取任务

        try {

        /// 如果timed为true,通过poll()方法做超时拉取,keepAliveTime时间内没有等待到有效的任务,则返回null

        // 如果timed为false,通过take()做阻塞拉取,会阻塞到有下一个有效的任务时候再返回(一般不会是null)

          Runnable r = timed ?

        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : //获取并删除 该队列的头,等待一定时间

          workQueue.take(); //阻塞直到队列不为空

          if (r != null)

            return r;

          timedOut = true; // 如果 r == null,说明已经超时,timedOut设置为true

        } catch (InterruptedException retry) {

        // 如果获取任务时当前线程发生了中断,则设置timedOut为false并返回循环重试

        timedOut = false;

        }

      }

    }

    这里重要的地方是第二个if判断,目的是控制线程池的有效线程数量。由上文中的分析可以知道,在执行execute方法时,如果当前线程池的线程数量超过了corePoolSize且小于maximumPoolSize,并且workQueue已满时,则可以增加工作线程,但这时如果超时没有获取到任务,也就是timedOut为true的情况,说明workQueue已经为空了,也就说明了当前线程池中不需要那么多线程来执行任务了,可以把多于corePoolSize数量的线程销毁掉,保持线程数量在corePoolSize即可。

    什么时候会销毁?

    当然是runWorker方法执行完之后,也就是Worker中的run方法执行完,由JVM自动回收。

    getTask方法返回null时,在runWorker方法中会跳出while循环,然后会执行processWorkerExit方法。

    2.4.5 processWorkerExit方法

    private void processWorkerExit(Worker w, boolean completedAbruptly) {

      // 如果completedAbruptly值为true,说明线程执行时出现了异常,需要将workerCount减1;

      // 如果线程执行时没有出现异常,说明在getTask()方法中已经已经对workerCount进行了减1操作,这里就不必再减了。

      if (completedAbruptly)

        decrementWorkerCount();

      final ReentrantLock mainLock = this.mainLock;

      mainLock.lock();

      try {

        //统计完成的任务数

        completedTaskCount += w.completedTasks;

        workers.remove(w); // 从workers中移除,表示着从线程池中移除了一个工作线程

      } finally {

        mainLock.unlock();

      }

      tryTerminate(); // 根据线程池状态进行判断是否结束线程池

      int c = ctl.get();

      //当线程池是RUNNING或SHUTDOWN状态时,

      if (runStateLessThan(c, STOP)) {

        if (!completedAbruptly) {

          int min = allowCoreThreadTimeOut ? 0 : corePoolSize;

          if (min == 0 && ! workQueue.isEmpty())

              //allowCoreThreadTimeOut=true,且等待队列有任务,至少保留一个worker

            min = 1;

          if (workerCountOf(c) >= min) // workerCount>=corePoolSize则返回

            return;

        }

        addWorker(null, false); //如果worker是异常结束,那么会直接addWorker

      }

    }

    至此,processWorkerExit执行完之后,工作线程被销毁,以上就是整个工作线程的生命周期,从execute方法开始,Worker使用ThreadFactory创建新的工作线程,runWorker通过getTask获取任务,然后执行任务,如果getTask返回null,进入processWorkerExit方法,整个线程结束,如图所示:

    2.4.6 tryTerminate方法

    final void tryTerminate() {

      for (;;) {

         int c = ctl.get();

             /*

          * 当前线程池的状态为以下几种情况时,直接返回:

          * 1. RUNNING,因为还在运行中,不能停止;

          * 2. TIDYING或TERMINATED,因为线程池中已经没有正在运行的线程了;

          * 3. SHUTDOWN并且等待队列非空,这时要执行完workQueue中的task;

          */

        if (isRunning(c) ||

          runStateAtLeast(c, TIDYING) ||

          (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))

          return;

          // 如果线程数量不为0,则中断一个空闲的工作线程,并返回

        if (workerCountOf(c) != 0) { // Eligible to terminate

          interruptIdleWorkers(ONLY_ONE); //中断空闲线程

          return;

        }

        final ReentrantLock mainLock = this.mainLock;

        mainLock.lock();

        try {

          // 这里尝试设置状态为TIDYING,如果设置成功,则调用terminated方法

          if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {

            try {

              terminated(); // 默认什么都不做,留给子类实现

            } finally {

            ctl.set(ctlOf(TERMINATED, 0)); // 设置状态为TERMINATED

            termination.signalAll();

            }

            return;

          }

        } finally {

          mainLock.unlock();

        }

        // else retry on failed CAS

      }

    }

    interruptIdleWorkers(ONLY_ONE); 作用是因为在getTask方法中执行 workQueue.take()时,如果不执行中断会一直阻塞。

    在下面介绍的shutdown方法中,会中断所有空闲的工作线程,如果在执行shutdown时工作线程没有空闲,然后又去调用了getTask方法,这时如果workQueue中没有任务了,调用workQueue.take() 时就会一直阻塞。所以每次在工作线程结束时调用tryTerminate方法来尝试中断一个空闲工作线程,避免在队列为空时取任务一直阻塞的情况。

    2.4.7 shutdown方法

    public void shutdown() {

      final ReentrantLock mainLock = this.mainLock;

      mainLock.lock();

      try {

        checkShutdownAccess(); // 安全策略判断

        advanceRunState(SHUTDOWN); // 切换状态为SHUTDOWN

        interruptIdleWorkers(); // 中断空闲线程

        onShutdown(); // hook for ScheduledThreadPoolExecutor

      } finally {

        mainLock.unlock();

      }

      tryTerminate(); // 尝试结束线程

    }

    这里思考一个问题:在runWorker方法中,执行任务时对Worker对象w进行了lock操作,为什么要在执行任务的时候对每个工作线程都加锁呢?

    下面仔细分析一下:

    • 在getTask方法中,如果这时线程池的状态是SHUTDOWN并且workQueue为空,那么就应该返回null来结束这个工作线程,而使线程池进入SHUTDOWN状态需要调用shutdown方法;
    • shutdown方法会调用interruptIdleWorkers来中断空闲的线程,interruptIdleWorkers持有mainLock,会遍历workers来逐个判断工作线程是否空闲。但getTask方法中没有mainLock;

    在getTask中,如果判断当前线程池状态是RUNNING,并且阻塞队列为空,那么会调用

    workQueue.take() 进行阻塞;

    • 如果在判断当前线程池状态是RUNNING后,这时调用了shutdown方法把状态改为了SHUTDOWN,这时如果不进行中断,那么当前的工作线程在调用了workQueue.take()

    后会一直阻塞而不会被销毁,因为在SHUTDOWN状态下不允许再有新的任务添加到workQueue中,这样一来线程池永远都关闭不了了;

    • 由上可知,shutdown方法与getTask方法(从队列中获取任务时)存在竞态条件;

    解决这一问题就需要用到线程的中断,也就是为什么要用interruptIdleWorkers方法。在调用

    workQueue.take() 时,如果发现当前线程在执行之前或者执行期间是中断状态,则会抛出InterruptedException,解除阻塞的状态;

    • 但是要中断工作线程,还要判断工作线程是否是空闲的,如果工作线程正在处理任务,就不应该发生中断;
    • 所以Worker继承自AQS,在工作线程处理任务时会进行lock,interruptIdleWorkers在进行中断时会使用tryLock来判断该工作线程是否正在处理任务,如果tryLock返回true,说明该工作线程当前未执行任务,这时才可以被中断。

    2.4.8 interruptIdleWorkers方法

    private void interruptIdleWorkers() {

      interruptIdleWorkers(false);

    }

    private void interruptIdleWorkers(boolean onlyOne) {

      final ReentrantLock mainLock = this.mainLock;

      mainLock.lock();

      try { // 遍历workers中所有的工作线程

        for (Worker w : workers) {

          Thread t = w.thread;

          // 若线程没有被中断且 tryLock成功,就中断该线程

          if (!t.isInterrupted() && w.tryLock()) {

            try {

              t.interrupt();

            } catch (SecurityException ignore) {

            } finally {

              w.unlock();

            }

          }

          if (onlyOne)

          break;

        }

      } finally {

        mainLock.unlock();

      }

    }

    为什么需要持有mainLock?因为workers是HashSet类型的,不能保证线程安全。

    2.4.9 shutdownNow方法

    public List<Runnable> shutdownNow() {

      List<Runnable> tasks;

      final ReentrantLock mainLock = this.mainLock;

      mainLock.lock();

      try {

        checkShutdownAccess(); // 安全策略判断

        advanceRunState(STOP); // 切换状态为STOP

        interruptWorkers(); // 中断所有工作线程,无论是否空闲

        tasks = drainQueue(); // 取出队列中没有被执行的任务

      } finally {

        mainLock.unlock();

      }

      tryTerminate(); // 尝试结束线程使线程池的状态设置为TERMINATED

      return tasks;

    }

    shutdownNow方法与shutdown方法类似,不同的地方在于:

    1. 设置状态为STOP;
    2. 中断所有工作线程,无论是否是空闲的;
    3. 取出阻塞队列中没有被执行的任务并返回。

    2.5 execute()方法

    2.5.1 execute() 提交任务

    public void execute(Runnable command) {

      if (command == null)

        throw new NullPointerException();

      int c = ctl.get(); // clt记录着runState和workerCount

      if (workerCountOf(c) < corePoolSize) { //活动线程 < 核心线程数, 新建一个线程放入线程池中,并把任务添加到该线程中

      // addWorker中的第二个参数表示限制添加线程的数量是根据corePoolSize来判断还是maximumPoolSize来判断; 如果为true,根据corePoolSize来判断;false,则根据maximumPoolSize来判断

        if (addWorker(command, true))

          return;

      c = ctl.get(); // 如果添加失败,则重新获取ctl值

      }

      // 如果当前线程池是运行状态并且任务添加到队列成功

      if (isRunning(c) && workQueue.offer(command)) {

        int recheck = ctl.get(); // 重新获取ctl值

        // 再次判断线程池的运行状态,如果不是 , 这时需要移除之前添加到workQueue中    的 command

        // 执行过后通过handler使用拒绝策略对该任务进行处理,整个方法返回

        if (! isRunning(recheck) && remove(command))

          reject(command);

          //获取线程池中的有效线程数,如果是0,则执行addWorker方法

        else if (workerCountOf(recheck) == 0)

          / * 第一个参数为null,表示在线程池中创建一个线程,但不去启动;

          * 第二个参数为false,将线程池的有限线程数量的上限设置为maximumPoolSize,添加线程时根据maximumPoolSize来判断;

          * 如果判断workerCount大于0,则直接返回,在workQueue中新增的command会在将来的某个时刻被执行。*/

        addWorker(null, false);

      }

      / * 如果执行到这里,有两种情况:

      * 1. 线程池已经不是RUNNING状态;

      * 2. 线程池是RUNNING状态,但workerCount >= corePoolSize并且workQueue已满。

      * 这时,再次调用addWorker方法,但第二个参数传入为false,将线程池的有限线程数量的上限设置为maximumPoolSize;

      */

      else if (!addWorker(command, false))

        reject(command); //如果失败则拒绝该任务

    }

    execute执行流程

    简单来说,在执行execute()方法时如果状态一直是RUNNING时,的执行过程如下:

    1. 如果workerCount < corePoolSize 则创建并启动一个线程来执行新提交的任务,即使线程池中的其他线程是空闲的;

    2.如果 workerCount >= corePoolSize 且线程池内的阻塞队列workQueue未满,则将任务添加到该阻塞队列中;

    3.如果 workerCount >= corePoolSize && workerCount < maximumPoolSize 且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务;

    4.如果 workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。

    这里要注意一下 addWorker(null, false);,也就是创建一个线程,但并没有传入任务,因为任务已经被添加到workQueue中了,所以worker在执行的时候,会直接从workQueue中获取任务。所以,在workerCountOf(recheck) == 0时执行 addWorker(null, false);

    也是为了保证线程池在RUNNING状态下必须要有一个线程来执行任务。

    任务提交时,判断的顺序为 corePoolSize –> workQueue –> maximumPoolSize。

    execute方法执行流程图如下:

    2.5.2 addWorker方法

    作用: 在线程池中创建一个新的线程并执行,

    参数:

    firstTask参数 用于指定新增的线程执行的第一个任务,

    core参数为true表示在新增线程时会判断当前活动线程数是否少于corePoolSize,false表示新增线程前需要判断当前活动线程数是否少于maximumPoolSize

    private boolean addWorker(Runnable firstTask, boolean core) {

      //外部循环体,这样看来,作用是不断的校验当前的线程池状态是否能接受新任务,如果校验通过了之后才能继续往下运行

      retry:

      for (int c = ctl.get();;) { // 获取运行状态 c

           //如果 状态值 >= SHUTDOWN,则表示此时不再接收新任务,但可以继续处理阻塞队列中已经保存的任务,

        // 所以在新任务firstTask不为空的时候会返回false;

        // STOP状态也返回false

        // 队列为空,表示没有任务,不需要再添加线程,返回false

        if (runStateAtLeast(c, SHUTDOWN)

          && (runStateAtLeast(c, STOP)

          || firstTask != null

          || workQueue.isEmpty()))

          return false;

        for (;;) {

        // 如果 工作线程 超过 一定数量,返回false;

        // core参数为true表示根据corePoolSize来比较,

        // core参数为false则根据maximumPoolSize来比较。

          if (workerCountOf(c)

            >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))

            return false;

          if (compareAndIncrementWorkerCount(c))

            break retry; // 尝试增加workerCount,如果成功,则跳出第一个for循环

          c = ctl.get(); // 如果增加workerCount失败,则重新获取ctl的值

          if (runStateAtLeast(c, SHUTDOWN))

            continue retry; // 如果线程池状态发生变化,重新从最外层循环

          // else CAS failed due to workerCount change; retry inner loop

        }

      } //外部循环体-end

      //-----------第二部分代码-----------

      boolean workerStarted = false;

      boolean workerAdded = false;

      Worker w = null;

      try {

        w = new Worker(firstTask); //根据firstTask来创建Worker对象

        final Thread t = w.thread; // 每一个Worker对象都会创建一个线程

        if (t != null) {

          final ReentrantLock mainLock = this.mainLock;

          mainLock.lock();

          try {

            int c = ctl.get();

          // 如果线程状态是RUNNING或者是SHUTDOWN状态并且firstTask为null,向线程池中添加线程。因为在SHUTDOWN时不会添加新的任务,但还是会执行workQueue中的任务

            if (isRunning(c) ||

              (runStateLessThan(c, STOP) && firstTask == null)) {

                if (t.isAlive()) // precheck that t is startable

                  throw new IllegalThreadStateException();

                workers.add(w);

                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);

      }

      return workerStarted;

    }

    4.其他

    4.1 线程池的监控

    通过线程池提供的参数进行监控。线程池里有一些属性在监控线程池的时候可以使用

    • getTaskCount:线程池已经执行的和未执行的任务总数;
    • getCompletedTaskCount:线程池已完成的任务数量,该值小于等于taskCount;
    • getLargestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过,也就是达到了maximumPoolSize;
    • getPoolSize:线程池当前的线程数量;
    • getActiveCount:当前线程池中正在执行任务的线程数量。

    通过这些方法,可以对线程池进行监控,在ThreadPoolExecutor类中提供了几个空方法,如beforeExecute方法,afterExecute方法和terminated方法,可以扩展这些方法在执行前或执行后增加一些新的操作,例如统计线程池的执行任务的时间等,可以继承自ThreadPoolExecutor来进行扩展。

    参考:

    http://www.ideabuffer.cn/2017/04/04/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Java%E7%BA%BF%E7%A8%8B%E6%B1%A0%EF%BC%9AThreadPoolExecutor/

    https://blog.csdn.net/lvxinchun/article/details/108830502

    https://mp.weixin.qq.com/s/31OUukd-bbBbs-pJpKE_cQ

    https://www.cnblogs.com/jajian/p/11442929.html

  • 相关阅读:
    正则结合
    解决Linux下yum安装无法解析URL的问题
    Linux安装PHP和MySQL
    Windows安装PHP MongoDB扩展
    转】关于cgi、FastCGI、php-fpm、php-cgi
    Linux安装PHP MongoDB扩展
    mysql InnoDB引擎 共享表空间和独立表空间(转载)
    Mysql优化ibdata1大小
    Magento-设置产品显示的条数和默认条数
    MySql创建指定字符集的数据库
  • 原文地址:https://www.cnblogs.com/coloz/p/14345683.html
Copyright © 2020-2023  润新知