线程池流程梳理

数据库连接池核心类ThreadPoolExecutor

  线程池的设计类似于生产者–消费者模型。生产者主要用于添加任务到线程池,消费者从线程队列中取出线程执行任务队列中的任务。
  我们知道,创建线程需要发出系统调用,陷入内核,调用内核 API 创建线程,为线程分配资源等,这一些操作有很大的开销。在高并发大流量的情况下,频繁的创建和销毁线程会大大拖慢响应速度。为了避免线程的创建和销毁,要把线程复用起来。

Executor 框架

  先看一下java中Executor框架的流程图。
在这里插入图片描述

Executor 接口

在这里插入图片描述

  Executor 是 java.util.concurrent 的顶级接口,它里面只定义了一个方法execute,负责接收提交的任务。不过execute方法只能接收实现Runnable接口的实现类,对于实现Callable 接口的类,需要它的子类方法sumbmit添加。
  Executor 的设计初衷就是将任务提交任务执行细节进行解藕

ExecutorService

public interface ExecutorService extends Executor {
    void shutdown();
    List<Runnable> shutdownNow();
    boolean isShutdown();
    boolean isTerminated();
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

  可以发现,ExecutorService 也是一个接口,并且继承Executor接口。扩展了不少功能。

  1. shutdown 方法调用后,ExecutorService 会有序关闭正在执行的任务,但是不接受新任务。如果任务已经关闭,那么这个方法不会产生任何影响。
  2. shutdownNow 会尝试停止关闭所有正在执行的任务,停止正在等待的任务,并返回正在等待执行的任务列表。
  3. isShutdown 方法表示执行器是否已经关闭,如果已经关闭,返回 true,否则返回 false。
  4. isTerminated 方法表示判断所有任务再关闭后是否已完成,如果完成返回 false。这个需要注意一点,除非首先调用 shutdown 或者 shutdownNow 方法,否则 isTerminated 方法永远不会为 true。
  5. awaitTermination 方法会阻塞,直到发出调用 shutdown 请求后所有的任务已经完成执行后才会解除。
  6. submit方法支持实现Runnable接口和Callable 接口的实现类。
  7. invokeAll 方法用于执行给定的任务集合,执行完成后会返回一个任务列表,任务列表每一项是一个任务,每个任务会包括任务状态和执行结果,同样 invokeAll 方法也会返回 Future 对象。
  8. invokeAny 会获得最先完成任务的结果,即Callable 接口中的 call 的返回值,在获得结果时,会中断其他正在执行的任务,具有阻塞性。

AbstractExecutorService

  AbstractExecutorService 是一个抽象类,它实现了 ExecutorService 中的部分方法。AbstractExecutorService 这个抽象类主要实现了 invokeAll 和 invokeAny 方法

ScheduledExecutorService

public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
                                           long delay, TimeUnit unit);
                                           public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);
                                                  public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);
  1. ScheduledExecutorService 也是一个接口,它扩展了 ExecutorService 接口,提供了 ExecutorService 接口所没有定时执行的功能。
  2. schedule 方法能够延迟一定时间后执行任务,并且只能执行一次。schedule 方法也返回了一个 ScheduledFuture 对象,ScheduledFuture 对象扩展了 Future 和 Delayed 接口,它表示异步延迟计算的结果。schedule 方法支持零延迟和负延迟,这两类值都被视为立即执行任务。
  3. scheduleAtFixedRate 表示任务会根据固定的速率在时间 initialDelay 后不断地执行。
  4. scheduleWithFixedDelay表示的是以固定延迟时间的方式来执行任务。

scheduleAtFixedRate 与 scheduleWithFixedDelay的区别:
(1)scheduleAtFixedRate 是以恒定的速率来执行任务的。
(2)scheduleWithFixedDelay 是以固定时延来执行的。(固定时间) + delay(time spend)。
假设每1s执行一次。
scheduleAtFixedRate 基本都是1s左右执行一次任务。
scheduleWithFixedDelay 如果任务执行完耗时2s,那么下一次执行时间为 1 +2 左右。

ThreadPoolExecutor与 ScheduledThreadPoolExecutor

  这两个是线程池的落地实现。下面是ThreadPoolExecutor的执行流程图梳理。

ThreadPoolExecutor的设计

  ThreadPoolExecutor通过一个原子性操作类ctl,保存了线程池的生命周期。
  ctl是AtomicInteger类型。它里面保存了线程池的生命周期和有效线程数量。代码中实现如下:

  private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    //Integer.SIZE = 32, COUNT_BITS = 29
    private static final int COUNT_BITS = Integer.SIZE - 3;
    //
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    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 & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

  我是这么理解的。取int的高三位。
  (1)000。表示状态SHUTDOWN
  (2)001。表示状态STOP
  (3)010。表示状态TIDYING
  (4)011。表示状态TERMINATED
  (5)101。表示状态RUNNING

  1. RUNNING。线程池处于RUNNING状态,能接收新任务,也能处理正在运行的任务。线程池已创建出来就处于RUNNING状态。
  2. SHUTDOWN。线程池调用shutdown方法后,线程池会从running–>shutdown。这时候,线程池能够处理正在运行的任务,但是不会接收新的任务。
  3. STOP。和shutdown类似,调用shutdownnow方法时,线程池会从running–>stop。这时候不接受新任务,不处理已添加到任务队列中的任务,中断正在执行的任务。
  4. TIDYING。线程池处于shutdown或者stop状态时,如果线程池中的线程数量为空时,会转换为TIDYING状态。转换为TIDYING的线程池会调用terminated这个钩子函数。
  5. TERMINATED。线程池处于TYDYING状态,执行完terminated方法后,状态转换为TERMINATED。此时线程池彻底结束。

执行流程:
在这里插入图片描述
  线程都保存再HashSet类型的集合中。对于创建线程的逻辑,都需要先获取mainLock锁,保证并发操作线程安全。

   private final ReentrantLock mainLock = new ReentrantLock();

    /**
     * Set containing all worker threads in pool. Accessed only when
     * holding mainLock.
     */
    private final HashSet<Worker> workers = new HashSet<Worker>();

    /**
     * Wait condition to support awaitTermination
     */
    private final Condition termination = mainLock.newCondition();
源码整体解析如下
public void execute(Runnable command) {
  if (command == null)
    throw new NullPointerException();
  // 获取 ctl 的值
  int c = ctl.get();
  // 判断 ctl 的值是否小于核心线程池的数量
  if (workerCountOf(c) < corePoolSize) {
    // 如果小于,增加工作队列,command 就是一个个的任务
    if (addWorker(command, true))
      // 线程创建成功,直接返回
      return;
    // 线程添加不成功,需要再次判断,每需要一次判断都会获取 ctl 的值
    c = ctl.get();
  }
  // 如果线程池处于运行状态并且能够成功的放入阻塞队列
  if (isRunning(c) && workQueue.offer(command)) {
    // 再次进行检查
    int recheck = ctl.get();
    // 如果不是运行态并且成功的从阻塞队列中删除
    if (! isRunning(recheck) && remove(command))
      // 执行拒绝策略
      reject(command);
    // worker 线程数量是否为 0
    else if (workerCountOf(recheck) == 0)
      // 增加工作线程
      addWorker(null, false);
  }
  // 如果不能增加工作线程的数量,就会直接执行拒绝策略
  else if (!addWorker(command, false))
    reject(command);
}

  addWorker的源码解析。

private boolean addWorker(Runnable firstTask, boolean core) {
  // retry 的用法相当于 goto
  retry:
  for (;;) {
    int c = ctl.get();
    int rs = runStateOf(c);

    // Check if queue empty only if necessary.
    // 仅在必要时检查队列是否为空。
    // 线程池状态有五种,state 越小越是运行状态
    // rs >= SHUTDOWN,表示此时线程池状态可能是 SHUTDOWN、STOP、TIDYING、TERMINATED
    // 默认 rs >= SHUTDOWN,如果 rs = SHUTDOWN,直接返回 false
    // 默认 rs < SHUTDOWN,是 RUNNING,如果任务不是空,返回 false
    // 默认 RUNNING,任务是空,如果工作队列为空,返回 false
    //
    if (rs >= SHUTDOWN &&
        ! (rs == SHUTDOWN &&
           firstTask == null &&
           ! workQueue.isEmpty()))
      return false;


    // 执行循环
    for (;;) {
      // 统计工作线程数量
      int wc = workerCountOf(c);
      // 如果 worker 数量>线程池最大上限 CAPACITY(即使用int低29位可以容纳的最大值)
      // 或者 worker数量 > corePoolSize 或 worker数量>maximumPoolSize ),即已经超过了给定的边界
      if (wc >= CAPACITY ||
          wc >= (core ? corePoolSize : maximumPoolSize))
        return false;

      // 使用 CAS 增加 worker 数量,增加成功,跳出循环。
      if (compareAndIncrementWorkerCount(c))
        break retry;

      // 检查 ctl
      c = ctl.get();  // Re-read ctl
      // 如果状态不等于之前获取的 state,跳出内层循环,继续去外层循环判断
      if (runStateOf(c) != rs)
        continue retry;
      // else CAS failed due to workerCount change; retry inner loop
    }
  }

  /*
          worker数量+1成功的后续操作
        * 添加到 workers Set 集合,并启动 worker 线程
         */
  boolean workerStarted = false;
  boolean workerAdded = false;
  Worker w = null;
  try {
    // 包装 Runnable 对象
    // 设置 firstTask 的值为 -1
    // 赋值给当前任务
    // 使用 worker 自身这个 runnable,调用 ThreadFactory 创建一个线程,并设置给worker的成员变量thread
    w = new Worker(firstTask);
    final Thread t = w.thread;
    if (t != null) {
      final ReentrantLock mainLock = this.mainLock;
      mainLock.lock();
      try {
        // 在持有锁的时候重新检查
        // 如果 ThreadFactory 失败或在获得锁之前关闭,请回退。
        int rs = runStateOf(ctl.get());

        //如果线程池在运行 running<shutdown 或者 线程池已经 shutdown,且firstTask==null
        // (可能是 workQueue 中仍有未执行完成的任务,创建没有初始任务的 worker 线程执行)
        //worker 数量 -1 的操作在 addWorkerFailed()
        if (rs < SHUTDOWN ||
            (rs == SHUTDOWN && firstTask == null)) {
          if (t.isAlive()) // precheck that t is startable
            throw new IllegalThreadStateException();

          // workers 就是一个 HashSet 集合
          workers.add(w);

          // 设置最大的池大小 largestPoolSize,workerAdded 设置为true
          int s = workers.size();
          if (s > largestPoolSize)
            largestPoolSize = s;
          workerAdded = true;
        }
      } finally {
        mainLock.unlock();
      }
      if (workerAdded) {
        t.start();
        workerStarted = true;
      }
    }
    //如果启动线程失败
    // worker 数量 -1
  } finally {
    if (! workerStarted)
      addWorkerFailed(w);
  }
  return workerStarted;
}

final void runWorker(Worker w) {
  Thread wt = Thread.currentThread();
  Runnable task = w.firstTask;
  w.firstTask = null;
  // 允许打断
  //  new Worker() 是 state==-1,此处是调用 Worker 类的 tryRelease() 方法,
  //  将 state 置为0
  w.unlock();
  boolean completedAbruptly = true;
  try {
    // 调用 getTask() 获取任务
    while (task != null || (task = getTask()) != null) {
      // 获取全局锁
      w.lock();
      // 确保只有在线程 STOPING 时,才会被设置中断标志,否则清除中断标志。
      // 如果一开始判断线程池状态 < STOPING,但 Thread.interrupted() 为 true,
      // 即线程已经被中断,又清除了中断标示,再次判断线程池状态是否 >= stop
      // 是,再次设置中断标示,wt.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,完成任务数 + 1,并进行解锁
        task = null;
        w.completedTasks++;
        w.unlock();
      }
    }
    completedAbruptly = false;
    // 最后处理 worker 的退出
  } finally {
    processWorkerExit(w, completedAbruptly);
  }
}
private Runnable getTask() {
  // 判断最后一个 poll 是否超时。
  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.
    // 必要时检查队列是否为空
    // 对线程池状态的判断,两种情况会 workerCount-1,并且返回 null
    // 线程池状态为 shutdown,且 workQueue 为空(反映了 shutdown 状态的线程池还是要执行 workQueue 中剩余的任务的)
    // 线程池状态为 stop(shutdownNow() 会导致变成 STOP)(此时不用考虑 workQueue 的情况)
    if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
      decrementWorkerCount();
      return null;
    }

    int wc = workerCountOf(c);

    // Are workers subject to culling?
    // 是否需要定时从 workQueue 中获取
    boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

    // 如果工作线程的数量大于 maximumPoolSize 会进行线程剔除
    // 如果使用了 allowCoreThreadTimeOut ,并且工作线程不为0或者队列有任务的话,会直接进行线程剔除
    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;
    }
  }
}
如何实现线程复用呢?

  我的理解是线程对象work,在从任务队列中获取任务时,如果线程数量 > 核心线程数量。通过workquue.poll(keepAliveTime)方法,设置超时时间获取任务。超时时间内没有返回任务,该线程work进行清除操作线程集合中移除线程work。如果线程数量 < 核心线程数,通过workQueue.take()阻塞等待获取任务。

使用线程池,如果获取执行返回结果

  我们可以看到线程池的submit方法,都有返回Future 接口。
  Future接口有5个方法。

  1. cancel
      取消任务
  2. isCannel
      判断任务是否已取消
  3. isDone
      判断任务是否结束
  4. get
      获取执行结果
  5. get(timeout ,unit)
      设置超时时间获取执行结果

  FutureTask实现了Future 和 Runnable接口。本身可以作为任务交给ThreadPooExecutor去执行。然后通过FutrueTask获取执行结果。

ThreadPoolExecutor流程图梳理如下

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值