ThreadPoolExecutor执行过程

本文深入剖析Java线程池的工作原理,包括线程池的创建、执行流程、线程管理和任务调度机制等,并提供了一个简单的使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

正在努力学习Java的小白程序员,最近在项目开发过程中,使用到了线程池。顺便看了遍源码,以此记录下做份笔记。

环境配置

jdk1.8

线程池创建

线程池的创建总的来说只有一种方法,以及在其之上衍生的其它JDK方法。

线程池创建的核心类是 ThreadPoolExecutor,线程池的创建以及运行都在该类中。所谓的线程池,在 ThreadPoolExecutor 内部,由一个集合 HashSet 存储。线程池内每个线程被一个类名Woker的类进行封装。

private final HashSet<Worker> workers = new HashSet<Worker>();
private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable{
  ...
  Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);}
  ...
}

线程池共有7个构造参数:

  1. 核心线程数:int corePoolSize

    线程池里常规存活数量,即使线程是处于空闲状态也不会被清除。除非设置了 allowCoreThreadTimeOut 为 true 值,将以 keepAliveTime 的标准去删除空闲线程。

    // If false (default), core threads stay alive even when idle.
    // If true, core threads use keepAliveTime to time out waiting
    // for work.
    private volatile boolean allowCoreThreadTimeOut;
    
  2. 线程池最大线程数:int maximumPoolSize

    允许线程池最大线程数

  3. 存活时间:long keepAliveTime

    超出核心线程池数量的部分线程,线程将在被终结之前等待新任务最大的空闲时间。

  4. 时间单位:TimeUnit unit

  5. 阻塞任务队列:BlockingQueue workQueue

  6. 线程工厂:ThreadFactory

  7. 拒绝策略:RejectedExecutionHandler

线程池内关键变量

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;									// 29
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;						// 536870911
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;

线程池内部封装类 Worker

private final class Worker
   extends AbstractQueuedSynchronizer	// 继承的是抽象同步队列
   implements Runnable
{
  // ...
  final Thread thread;
  Runnable firstTask;
  Worker(Runnable firstTast) {
    setState(-1);		// inhibit interrupts until runWorker
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
  }
  // ...
}

该内部类 Worker 继承了 AbstractQueuedSynchronized 抽象同步队列。不过,ThreadPoolExecutor 并没有使用到该抽象类的功能。

protected boolean isHeldExclusively() { return getState() != 0; }
void interruptIfStarted() {
  Thread t;
  if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
    try { t.interrupt(); } catch (SecurityException ignore) {}
}}
protected boolean tryAcquire(int unused) {
  if (compareAndSetState(0, 1)) {
    setExclusiveOwnerThread(Thread.currentThread()); // 设置线程池拥有者持有该Worker
    return true; }
  return false; }
protected boolean tryRelease(int unused) {
  setExclusiveOwnerThread(null);	// 调用顶级父类方法
  setState(0);
  return true; }

上述是对线程状态的判断,借用了父类抽象同步队列的 state 变量,用于对线程状态的判断、线程拥属、并发访问(tryAcquire()、tryRelease())。

线程创建

线程池的容量本身最高为536870911,存储于 CAPACITY 常量中。而决定实际容量的是 corePoolSize 核心线程池以及 maximumPoolSize 最大线程池数。该常量值用于作求与运算。

线程池在最初创建时并不会立刻生成线程,而是在需要的时候,调用 addWorker(Runnable, boolean) 方法,才会创建。如在调用 execute(Runnable) 方法时判断,或在调用 setCorePoolSize(int) 设立新的线程池的时候调用该方法。

// 部分方法 1
private boolean addWorker(Runnable firstTask, boolean core) {
  retry:
  for (;;) { 
    int c = ctl.get(); int rs = runStateOf(c);
    // Check if queue empty only if necessary.
    if (rs >= SHUTDOWN &&
        // 判断线程是否是已空
        !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
      return false;
    for (;;) {
      int wc = workerCountOf(c);
      if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
      { return false; }
      if (compareAndIncrementWorkerCount(c))
      { break retry; }
      c = ctl.get();  // Re-read ctl
      if (runStateOf(c) != rs)
      { continue retry; }
      // else CAS failed due to workerCount change; retry inner loop
    }

该步骤只是为了使线程数量增加1。通过CAS和自旋的方式,使 ctl 自增1。

// 续上
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
  // 生成 Worker 对象实例
  w = new Worker(firstTask);
  final Thread t = w.thread;
  if (t != null) {
    final ReentrantLock mainLock = this.mainLock;
    // 上锁,多线程并发互斥操作
    mainLock.lock();
    try {
      // Recheck while holding lock.
      // Back out on ThreadFactory failure or if
      // shut down before lock acquired.
      int rs = runStateOf(ctl.get());
      if (rs < SHUTDOWN ||
          (rs == SHUTDOWN && 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;

该步骤是为线程池添加新的工作线程。

线程池执行入口 execute(Runnable)

线程池在创建之后,只要传递实现了 Runnable接口 的类给 execute(Runnable) 即可。

public void execute(Runnable command) {
  if (command == null)
    throw new NullPointerException();
  int c = ctl.get();
  if (workerCountOf(c) < corePoolSize) { 
    if (addWorker(command, true)) { return; }
    c = ctl.get();}
  if (isRunning(c) && workQueue.offer(command)) {
    int recheck = ctl.get();
    if (! isRunning(recheck) && remove(command)) { reject(command); }
    else if (workerCountOf(recheck) == 0) { addWorker(null, false); }
  }
  else if (!addWorker(command, false)) { reject(command); }}

从使用者角度来看,线程池数量应该是由核心线程池、最大线程池数量以及阻塞任务队列来决定的。**当线程池刚创建时,线程数量为零,然后在反复调用 execute 方法后,线程数量将会上升到核心线程数量;随后线程池内线程数量不再增加,线程池便会把任务放入阻塞任务队列中;当阻塞任务队列数额已满或因其它原因不能放入时,线程池中的工作线程数量才会增加,直至达到最大线程数。**整个方法分为三个步骤执行,如下所示:

  1. 若线程池线程数小于核心线程数,便调用 addWorker(Runnable, bool) 方法,生成 Worker 对象并向 Wroker 对象传入执行计划以及设置 Wroker 封装的线程为就绪态。
  2. 若步骤一执行失败,判断线程池是否是在运行状态以及是否能往阻塞任务队列添加任务。添加成功后需要进行第二次检测,以防止添加线程后线程池停止运行或者线程池里最后仅有的一个线程进入了结束态。
  3. 步骤三执行的时候,线程池内的线程数量已经超出了核心线程池数,且应该是小于最大线程池数,调用addWorker(Runnable, bool)。

线程运行态

线程池的运行单位是内部类 Worker 实例。Worker 类继承了 Runnable 接口,如下所示:

private final class Worker
  extends AbstractQueuedSynchronizer
  implements Runnable
{
  /** Delegates main run loop to outer runWorker  */
  public void run() { runWorker(this); }
}

该实现接口将调用外围接口 ThreadPoolExecutor 的方法 runWorker(Worker):

final void runWorker(Worker w) {
  // wt:Worker 内部封装的线程
  Thread wt = Thread.currentThread();
  // task:Worker 内部封装的线程任务,跟上述的 wt 同一作用域
  Runnable task = w.firstTask;
  // 清除,表示正在消费或已消费
  w.firstTask = null;
  w.unlock(); // allow interrupts
  boolean completedAbruptly = true;
  try {
    // 自旋的开始,等待工作任务的到来
    // 若 Worker 作用域内没有任务,则从阻塞任务队列中取
    // 若 Worker 在 keeepAliveTime 内没有获取到,则退出循环
    while (task != null || (task = getTask()) != null) {
      // 上锁,即修改内部状态status,调用 Worker 内部的方法
      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
      if ((runStateAtLeast(ctl.get(), STOP) // 线程池处于停止状态
           || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) 
          && !wt.isInterrupted())
      	{ wt.interrupt(); }	// 线程进入中断状态
      try {
        beforeExecute(wt, task);	// 空方法,线程运行前执行
        Throwable thrown = null;	// 用于接收
        try { task.run();	} // 线程执行
        catch (RuntimeException x) { thrown = x; throw x; } 
        catch (Error x) { thrown = x; throw x; } 
        catch (Throwable x) { thrown = x; throw new Error(x); } 
        finally { afterExecute(task, thrown); }
      } finally { task = null; w.completedTasks++; w.unlock(); }
    }
    completedAbruptly = false;	// 设置该线程是否是因为超时而要停止的
  } finally { // 移除该 Worker 出线程池
    processWorkerExit(w, completedAbruptly); }}}

线程的生命周期要有足够的了解:初始态、就绪态、运行态、阻塞态以及终止态。

线程销毁

当 Worker 对象在执行 runWorker 的末尾时,便会执行 ThreadPoolExecutor 的方法 processWorkerExit(Worker w, boolean completedAbruptly)。 completedAbruptly指明该线程是突然间被终结的,需要主动的将线程池内线程数量减一。此时正在运行该代码块的是 Worker 内部封装的 thread 线程变量。

private void processWorkerExit(Worker w, boolean completedAbruptly) {
  if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
    // 非正常情况结束需要去减一
  { decrementWorkerCount(); }
	// 获取全局锁
  final ReentrantLock mainLock = this.mainLock;
  mainLock.lock();
  try {
    completedTaskCount += w.completedTasks;
    workers.remove(w); // 移除自身出线程池
  } finally { mainLock.unlock(); }

  tryTerminate();	// 可能终止线程池,运行态则不终止

  int c = ctl.get();
  if (runStateLessThan(c, STOP)) { // 若仍在运行状态内
    if (!completedAbruptly) {	// 若是在正常情况下结束任务
      int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
      if (min == 0 && !workQueue.isEmpty()) { min = 1; } // 线程数为0且存在任务
      if (workerCountOf(c) >= min) { return; } // replacement not needed
      // 只要线程池内有 线程运行线程任务 或 既无任务既无线程 时,则不用增加线程
    }
    // 回调,增加一个工作线程,是因为当前线程遇到非正常原因而关闭的
    addWorker(null, false);}}

线程任务获取

线程任务获取方法仅在 runWorker 方法中被调用。该方法以自旋的方式,将从阻塞任务队列中获取任务。若当前线程池线程数比核心线程数设定值要大,则执行到该步的线程则会有期限地从阻塞队列中获取任务。这意味着因获取不到任务而被移除的线程是随机的、非公平的,不会因为线程(Worker)生成的前后顺序而被顺序移除。

private Runnable getTask() {
  boolean timedOut = false; // Did the last poll() time out?

  for (;;) {
    int c = ctl.get();
    int rs = runStateOf(c);

    // Check if queue empty only if necessary.
    // 线程池无运行则该线程退出
    if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) 
    { decrementWorkerCount(); return null; }
    // 获取工作线程数
    int wc = workerCountOf(c);
    // 判断该线程是否可能被移除
    // Are workers subject to culling?
    boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
    // 在获取任务前判断该线程是否应该被移除
    // 只有判断线程需要被移除 timed == true,超时 timedOut 才会被判定
    if ((wc > maximumPoolSize || (timed && timedOut))
        && (wc > 1 || workQueue.isEmpty())) {
      if (compareAndDecrementWorkerCount(c)) { return null; }
      continue;
    }

    try {
      Runnable r = timed ?
        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
      workQueue.take();
      if (r != null) { return r; }
      timedOut = true;
    } catch (InterruptedException retry) {
      timedOut = false;
    }
  }
}

全局变量 allowCoreThreadTimeOut 为 true,意味着核心线程池也被列入超时获取不到线程而被移除的范围,即全部线程在规定时间内获取不到任务则会被移除。

线程拒绝策略

pass

线程池销毁

pass

线程池使用实例

private final static int DEFAULT_POOL_SIZES;
private final static int AVAILABLE_PROCESSORS;
final ThreadPoolExecutor executor = new ThreadPoolExecutor(
  DEFAULT_POOL_SIZES, AVAILABLE_PROCESSORS, 300L, TimeUnit.SECONDS,
  new LinkedBlockingQueue<>(), // 默认阻塞任务队列长度为 Integer.MAX_VALUE
  new ThreadFactoryBuilder().setNameFormat("TestInstanceThreadPool").build(),
  new TestRunsPolicy()
);
class TestRunsPolicy implements RejectedExecutionHandler {
  public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { ... }
}
class TestHandler implements Runnable {
  TestHandler(Object task) { ... }
}
executor.execute(new TestHandler(task));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值