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

posted @ 2021-01-29 16:30  将军上座  阅读(106)  评论(0编辑  收藏  举报