Executor框架

10.1 Executor框架的背景

  1. 在Java中,使用线程来异步执行任务。
  2. Java的线程既是工作线程,也是执行机制。
    • 如果我们为每个任务创建一个新线程来执行,则线程的创建与销毁将消耗大量的计算资源,并且这种策略可能会使处于高负荷状态的应用最终崩溃。
  3. 从JDK 5开始,把工作单元与执行机制分离开来
    • 工作单元包括Runnable和Callable,而执行机制由Executor框架提供

10.2 Executor框架简介

10.2.1 Executor框架的两级调度模型

  1. 在HotSpot VM的线程模型中,Java线程(java.lang.Thread)被一对一映射为本地操作系统线程
    • Java线程启动时会创建一个本地操作系统线程;
    • 当该Java线程终止时,这个操作系统线程也会被回收。
    • 操作系统会调度所有线程并将它们分配给可用的CPU。
  2. 两级调度模型
    1. 上层:Java多线程程序通常把应用分解为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程;
    2. 底层操作系统内核将这些线程映射到硬件处理器上。
  • 任务的两级调度模型的总结
    • 应用程序通过Executor框架控制上层的调度;
    • 下层的调度由操作系统内核控制
      这里写图片描述

10.2.2 Executor框架的组成

  1. 任务
    • 执行任务需要实现的接口:Runnable接口或Callable接口。
  2. 任务的执行
    • Executor接口:任务执行机制的核心接口Executor
    • ExecutorService接口:继承Executor接口。
    • ThreadPoolExecutor类和ScheduledThreadPoolExecutor类:实现ExecutorService接口的两个关键类。
  3. 异步计算的结果
    • Future接口
    • FutureTask类:实现Future接口。

10.2.3 Executor框架中主要的类与接口

  1. Executor接口:是Executor框架的基础。
  2. ThreadPoolExecutor类:是线程池的核心实现类,用来执行被提交的任务。
  3. ScheduledThreadPoolExecutor类:是线程池的实现类,可以在给定的延迟后运行命令,或者定期执行命令。
  4. Future接口和实现Future接口的FutureTask类:代表异步计算的结果。
  5. Runnable接口和Callable接口的实现类:都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。
    这里写图片描述

10.2.4 Executor框架的使用

  1. 主线程创建实现Runnable或者Callable接口的任务对象

    • 将一个Runnable对象封装为一个Callable对象:
    Executors.callable(Runnable task)
    Executors.callable(Runnable task,Object result)
    
  2. 提交任务(二选一)

    • 把Runnable对象直接交给ExecutorService执行
    ExecutorService.execute(Runnable command)
    
    • 把Runnable对象或Callable对象提交给ExecutorService执行
    ExecutorService.submit(Runnable task)
    ExecutorService.submit(Callable<T> task)
    
  3. 返回实现Future接口的对象

    • 如果执行ExecutorService.submit(…),ExecutorService将返回一个实现Future接口的对象(到目前为止的JDK中,返回的是FutureTask对象)。
  4. 主线程等待任务执行完成或取消此任务的执行

    FutureTask.get()方法:等待任务执行完成。
    FutureTask.cancel(boolean mayInterruptIfRunning):取消此任务的执行。
    

总结Executor框架的使用示意图
这里写图片描述

10.2.5 Executor框架的成员

1. ThreadPoolExecutor
  1. 通常使用工厂类Executors来创建。
  2. ThreadPoolExecutor的类型:SingleThreadExecutor、FixedThreadPool、CachedThreadPool。
    1. FixedThreadPool

      • 适用场景:为了满足资源管理的需求,而需要限制当前线程数量的应用场景,它适用于负载比较重的服务器
      • 创建使用固定线程数的FixedThreadPool
      //Executor类
      public static ExecutorService newFixedThreadPool(int nThreads)
      public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactorythreadFacty)
      
    2. SingleThreadExecutor

      • 适用场景:需要保证顺序地执行各个任务;并且在任意时间点,不会有多个线程是活动的应用场景
      • 创建使用单个线程的SingleThreadExecutor
      public static ExecutorService newSingleThreadExecutor()
      public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
      
    3. CachedThreadPool

      • 适用场景:很多的短期异步任务的小程序,或者是负载较轻的服务器
      • 创建一个会根据需要创建新线程的CachedThreadPool
      public static ExecutorService newCachedThreadPool()
      public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
      
2. ScheduledThreadPoolExecutor
  1. ScheduledThreadPoolExecutor通常使用工厂类Executors来创建。
  2. ScheduledThreadPoolExecutor的两种类型:
    1. ScheduledThreadPoolExecutor
      • 适用场景:需要多个后台线程执行周期任务同时为了满足资源管理的需求而需要限制后台线程的数量的应用场景。

      • 创建固定个数线程的ScheduledThreadPoolExecutor

        public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
        public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize,ThreadFactory threadFactory)
        
    2. SingleThreadScheduledExecutor
      • 适用场景:需要单个后台线程执行周期任务同时需要保证顺序地执行各个任务的应用场景。

      • 创建单个线程的SingleThreadScheduledExecutor

        public static ScheduledExecutorService newSingleThreadScheduledExecutor()
        public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
        
3. Future接口
  1. Future接口和实现Future接口的FutureTask类:表示异步计算的结果。
  2. submit()的语法
<T> Future<T> submit(Callable<T> task)
<T> Future<T> submit(Runnable task, T result)
Future<?> submit(Runnable task)
4. Runnable接口和Callable接口
  1. Runnable接口的实现类 & Callable接口的实现类

    • 相同:都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。
    • 区别:Runnable不会返回结果,而Callable可以返回结果。
  2. 获取Callable对象

    1. 法1:直接创建实现Callable接口的对象
    2. 法2:使用工厂类Executors来把一个Runnable包装成一个Callable对象
    //1. 把一个Runnable包装成一个Callable对象
    public static Callable<Object> callable(Runnable task) // 假设返回对象Callable1
    
    //2. 把一个Runnable和一个待返回的结果包装成一个Callable对象
    public static <T> Callable<T> callable(Runnable task, T result) // 假设返回对象Callable2
    

注意:当我们把一个Callable对象(比如上面的Callable1或Callable2)提交给ThreadPoolExecutor或ScheduledThreadPoolExecutor执行时,submit(…)会向我们返回一个FutureTask对象。当任务成功完成后FutureTask.get()将返回该任务的结果

  • 如果提交的是对象Callable1,FutureTask.get()方法将返回null
  • 如果提交的是对象Callable2,FutureTask.get()方法将返回result对象

10.3 ThreadPoolExecutor详解

  1. Executor框架最核心的类是ThreadPoolExecutor,它是线程池的实现类。

10.3.1 ThreadPoolExecutor的组件构成

  1. corePool:核心线程池。
  2. maximumPool:最大线程池。
  3. BlockingQueue:用来暂时保存任务的工作队列。
  4. RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时(达到了最大线程池大小且工作队列已满),execute()方法将要调用的Handler。

10.3.2 ThreadPoolExecutor的3种类型

  1. FixedThreadPool。
  2. SingleThreadExecutor。
  3. CachedThreadPool
10.3.2.1 FixedThreadPool详解
  1. FixedThreadPool的概述

    • 可重用固定线程数的线程池
    • 其corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool时指定的参数nThreads
    • 工作队列:无界队列LinkedBlockingQueue(队列的容量为Integer.MAX_VALUE)
  2. 构造函数

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
    }
    
  3. keepAliveTime设置

    • 当线程池中的线程数大于corePoolSize时,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止。
    • FixedThreadPool把keepAliveTime设置为0L,意味着多余的空闲线程会被立即终止。
  4. FixedThreadPool的execute()方法的运行

    • 步骤
    1. 如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务
    2. 在线程池完成预热之后(当前运行的线程数等于corePoolSize),将任务加入LinkedBlockingQueue
    3. 线程执行完1中的任务后,会在循环中反复从LinkedBlockingQueue获取任务来执行
    • 运行示意图
      这里写图片描述
  5. 使用无界队列作为工作队列会对线程池带来的影响

    • 线程池中的线程数不会超过corePoolSize。
    • maximumPoolSize将是一个无效参数。
    • keepAliveTime将是一个无效参数。
    • 运行中的FixedThreadPool(未执行方法shutdown()或
      shutdownNow())不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution方法)。
10.3.2.2 SingleThreadExecutor详解
  1. SingleThreadExecutor概述

    • 是使用单个worker线程的Executor
    • 其corePoolSize和maximumPoolSize被设置为1
    • 工作队列:无界队列LinkedBlockingQueue(队列的容量为Integer.MAX_VALUE)。
  2. 构造函数

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
    }
    
  3. SingleThreadExecutor的execute()的运行

    • 步骤
      1. 如果当前运行的线程数少于corePoolSize(即线程池中无运行的线程),则创建一个新线程来执行任务
      2. 在线程池完成预热之后(当前线程池中有一个运行的线程),将任务加入LinkedBlockingQueue
      3. 线程执行完1中的任务后,会在一个无限循环中反复从LinkedBlockingQueue获取任务来执行
    • 运行示意图
      这里写图片描述
10.3.2.3 CachedThreadPool详解
  1. CachedThreadPool概述

    • 根据需要创建新线程的线程池
    • corePoolSize被设置为0,即corePool为空;maximumPoolSize被设置为Integer.MAX_VALUE,即maximumPool是无界的
    • keepAliveTime设置为60L
      • 意味着:CachedThreadPool中的空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被终止。
    • 工作队列:SynchronousQueue(没有容量
  2. 构造方法

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
    }
    
  3. 特点/缺点

    • CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,但CachedThreadPool的maximumPool是无界的。
    • 意味着:如果主线程提交任务的速度高于maximumPool中线程处理任务的速度时,CachedThreadPool会不断创建新线程。极端情况下,CachedThreadPool会因为创建过多线程而耗尽CPU和内存资源
  4. CachedThreadPool的execute()方法的执行

    • 步骤
      1. 执行SynchronousQueue.offer(Runnable task)
        • 如果当前maximumPool中有空闲线程正在执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主线程执行offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute()方法执行完成;
        • 否则执行下面的步骤2
      2. 当初始maximumPool为空,或者maximumPool中当前没有空闲线程时,将没有线程执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这种情况下,步骤1将失败。此时CachedThreadPool会创建一个新线程执行任务,execute()方法执行完成。
      3. 步骤2中新创建的线程将任务执行完后,会执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这个poll操作会让空闲线程最多在SynchronousQueue中等待60秒钟
        • 如果60秒钟内主线程提交了一个新任务(主线程执行步骤1)),那么这个空闲线程将执行主线程提交的新任务;
        • 否则,这个空闲线程将终止。由于空闲60秒的空闲线程会被终止,因此长时间保持空闲的CachedThreadPool不会使用任何资源。
    • 示意图
      这里写图片描述
  5. CachedThreadPool中的任务传递

    • 概述
      • SynchronousQueue是一个没有容量的阻塞队列。
      • 每个插入操作必须等待另一个线程的对应移除操作,反之亦然。
      • CachedThreadPool使用SynchronousQueue,把主线程提交的任务传递给空闲线程执行
    • 任务传递示意图
      这里写图片描述

10.4 ScheduledThreadPoolExecutor详解

10.4.1 ScheduledThreadPoolExecutor概述

  1. ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。
  2. 功能:用于在给定的延迟之后运行任务,或者定期执行任务
  3. ScheduledThreadPoolExecutor & Timer
    • 相同:它们的功能类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。
    • 不同
      • Timer对应的是单个后台线程;
      • ScheduledThreadPoolExecutor可以指定多个后台线程。

10.4.2 ScheduledThreadPoolExecutor的运行机制

  1. 工作队列:无界队列DelayQueue。
  2. ScheduledThreadPoolExecutor的执行
    • 步骤
      1. 向DelayQueue添加任务。当调用ScheduledThreadPoolExecutor的scheduleAtFixedRate()方法或者scheduleWithFixedDelay()方法时,会向ScheduledThreadPoolExecutor的DelayQueue添加一个实现了RunnableScheduledFutur接口的ScheduledFutureTask
      2. 线程池中的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务。
    • 示意图
      这里写图片描述

10.4.3 ScheduledThreadPoolExecutor的实现

10.4.3.1 ScheduledThreadPoolExecutor的源代码
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService{
    //1.DelayedWorkQueue(工作队列)
    private static class DelayedWorkQueue extends AbstractCollection<Runnable> implements BlockingQueue<Runnable>{
        private final DelayQueue<RunnableScheduledFuture> dq;
    }
    
    //2. ScheduledFutureTask(任务)
    private class ScheduledFutureTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V> {
        private long time; //这个任务将要被执行的时间点
        private final long sequenceNumber; //任务的序号
        private final long period; //表示任务执行的间隔周期
    }
}
10.4.3.2 DeleyQueue的内部结构
  1. 源代码

    public class DelayQueue<E extends Delayed> extends AbstractQueue<E>  implements BlockingQueue<E>{
        private transient final ReentrantLock lock = new ReentrantLock();
        private transient final Condition available = lock.newCondition();
        private final PriorityQueue<E> q = new PriorityQueue<E>();
    }
    
  2. DelayQueue封装了一个PriorityQueue,这个PriorityQueue会对队列中的ScheduledFutureTask进行排序

    • 排序时,time小的排在前面(时间早的任务将被先执行)。
    • 如果两个ScheduledFutureTask的time相同,就比较sequenceNumber,sequenceNumber小的排在前面
    • 如果两个任务的执行时间相同,那么先提交的任务将被先执行。
10.4.3.3 ScheduledThreadPoolExecutor中的线程执行周期任务的过程
  1. 线程1执行周期任务的总过程

    1. 获取任务:线程1从DelayQueue中获取已到期的ScheduledFutureTask(DelayQueue.take())。到期任务是指ScheduledFutureTask的time大于等于当前时间。
    2. 执行任务:线程1执行这个ScheduledFutureTask。
    3. 修改time:线程1修改ScheduledFutureTask的time变量为下次将要被执行的时间。
    4. 添加任务:线程1把这个修改time之后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())

    示意图
    这里写图片描述

  2. 步骤1(获取任务)

    • DelayQueue.take()的源代码

      public E take() throws InterruptedException {
          final ReentrantLock lock = this.lock;
          lock.lockInterruptibly(); // 1:获取Lock
          try {
              for (;;) {
                  E first = q.peek();
                  if (first == null) {
                      available.await(); // 2.1:如果PriotiryQueue为空,则当前线程到Condition中等待
                  } else {
                      long delay = first.getDelay(TimeUnit.NANOSECONDS);
                      if (delay > 0) {
                          long tl = available.awaitNanos(delay); // 2.2:如果PriotiryQueue头元素的任务未到期,则当前线程到Condition中等待至time时间
                      } else {
                          E x = q.poll(); // 2.3.1:获取PriotiryQueue的头元素
                          assert x != null;
                          if (q.size() != 0)
                              available.signalAll(); // 2.3.2:如果PriotiryQueue不为空,则唤醒在Condition中等待的所有线程
                          return x;
                      }
                  }
              }
          } finally {
              lock.unlock(); // 3:释放锁
          }
      }
      
    • 步骤

      1. 获取Lock
      2. 获取周期任务
        • 2.1 如果PriorityQueue为空,当前线程到Condition中等待;否则执行下面的2.2。
        • 2.2 如果PriorityQueue的头元素的time时间比当前时间大,到Condition中等待到time时间;否则执行下面的2.3。
        • 2.3 获取PriorityQueue的头元素(2.3.1);如果PriorityQueue不为空,则唤醒在Condition中等待的所有线程(2.3.2)。
      3. 释放Lock
    • 示意图(DelayQueue.take()的执行)
      这里写图片描述

  3. 步骤4(添加任务)

    • DelayQueue.add()的源代码
      public boolean add(E e) {
          return offer(e);
      }
      
      public boolean offer(E e) {
          final ReentrantLock lock = this.lock;
          lock.lock(); // 1:获取Lock
          try {
              E first = q.peek();
              q.offer(e); // 2.1:添加任务
              if (first == null || e.compareTo(first) < 0)
                  available.signalAll(); // 2.2:如果添加的任务是PriorityQueue的头元素,则唤醒所有等待的线程
              return true;
          } finally {
              lock.unlock(); // 3:释放锁
          }
      }
      
    • 示意图(DelayQueue.add()的执行)
      这里写图片描述

10.5 FutureTask详解

10.5.1 FutureTask简介

  1. Future接口和实现Future接口的FutureTask类,代表异步计算的结果。
  2. FutureTask实现了Future接口和Runnable接口。
    • Runnable接口

      public interface Runnable {  
          public abstract void run();  
      }  
      
    • Callable接口

      public interface Callable<V> {   
          V  call()  throws Exception;   
      }
      
    • Future接口

      public interface Future<V> {  
          boolean cancel(boolean mayInterruptIfRunning);  //参数表示是否中断执行中的线程
          boolean isCancelled();  //如果任务完成前被取消,则返回true
          boolean isDone();  //如果任务执行结束,无论是正常结束,或中途取消,或发生异常,都返回true
          V get(); //获取异步执行的结果.如果没有结果可用,此方法会阻塞直到异步计算完成
          V get(long timeout, TimeUnit unit); //  获取异步执行结果.如果没有结果可用,此方法会阻塞,但是会有时间限制,如果阻塞时间超过设定的timeout时间,该方法将抛出异常
      } 
      
    • FutureTask类

      public class FutureTask<V> implements RunnableFuture<V> {...}
      
      public interface RunnableFuture<V> extends Runnable, Future<V> {
          void run();  
      }
      
  3. FutureTask也可以直接提交给Executor(Executor.execute(Runnable command))执行,也可以调用线程直接执行(FutureTask.run())。

10.5.2 FutureTask的状态迁移

  1. 根据FutureTask.run()方法被执行的时机,FutureTask可以处于下面3种状态
    • 未启动。当创建一个FutureTask,且没有执行FutureTask.run()方法之前,这个FutureTask处于未启动状态。
    • 已启动FutureTask.run()方法被执行的过程中,FutureTask处于已启动状态。
    • 已完成。以下几种情况都属于已完成状态。
      • FutureTask.run()方法执行完后正常结束
      • 被取消(FutureTask.cancel(…))
      • 执行FutureTask.run()方法时抛出异常而异常结束
  2. FutureTask的状态迁移示意图
    这里写图片描述
  3. FutureTask.get()
    • 当FutureTask处于未启动或已启动状态时,执行FutureTask.get()方法将导致调用线程阻塞
    • 当FutureTask处于已完成状态时,执行FutureTask.get()方法将导致调用线程立即返回结果或抛出异常
  4. FutureTask.cancel()方法
    • 当FutureTask处于未启动状态时,执行FutureTask.cancel()方法将导致此任务永远不会被执行
    • 当FutureTask处于已启动状态时
      • 执行FutureTask.cancel(true)方法将以中断执行此任务线程的方式来试图停止任务
      • 执行FutureTask.cancel(false)方法将不会对正在执行此任务的线程产生影响(让正在执行的任务运行完成)
    • 当FutureTask处于已完成状态时,执行FutureTask.cancel(…)方法将返回false
  5. FutureTask的get和cancel的执行示意图
    这里写图片描述

10.5.3 FutureTask的使用

  1. 场景:假设有多个线程执行若干任务每个任务最多只能被执行一次当多个线程试图同时执行同一个任务时,只允许一个线程执行任务,其他线程需要等待这个任务执行完后才能继续执行

  2. 示例代码

    private final ConcurrentMap<Object, Future<String>> taskCache = new ConcurrentHashMap<Object, Future<String>>();
    
    private String executionTask(final String taskName) throws ExecutionException, InterruptedException {
        while (true) {
            Future<String> future = taskCache.get(taskName); // 1.1, 2.1
            if (future == null) {
                Callable<String> task = new Callable<String>() {
                    public String call() throws InterruptedException {
                        return taskName;
                    }
                };
                
                //1.2 创建任务
                FutureTask<String> futureTask = new FutureTask<String>(task);
                future = taskCache.putIfAbsent(taskName, futureTask); // 1.3
                if (future == null) {
                    future = futureTask;
                    futureTask.run(); // 1.4 执行任务
                }
            }
            
            try {
                return future.get(); // 1.5,2.2  线程在此等待任务执行完成
            } catch (CancellationException e) {
                taskCache.remove(taskName, future);
            }
        }
    }
    
  3. 代码执行示意图
    这里写图片描述

  4. 解释:当两个线程试图同时执行同一个任务时,如果Thread1执行1.3后Thread 2执行2.1,那么接下来Thread 2将在2.2等待,直到Thread 1执行完1.4后Thread 2才能从2.2(FutureTask.get())返回

10.5.4 FutureTask的实现

  1. FutureTask的实现概述

    • FutureTask的实现基于AbstractQueuedSynchronizer(以下简称为AQS)
      • AQS是一个同步框架,它提供通用机制来原子性管理同步状态、阻塞和唤醒线程,以及维护被阻塞线程的队列。
      • JDK 6中AQS被广泛使用,基于AQS实现的同步器包括:ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore和FutureTask
    • 每一个基于AQS实现的同步器都会包含两种类型的操作:
      • 至少一个acquire操作。这个操作阻塞调用线程,除非/直到AQS的状态允许这个线程继续执行。FutureTask的acquire操作为get()/get(long timeout,TimeUnit unit)方法调用
      • 至少一个release操作。这个操作改变AQS的状态,改变后的状态可允许一个或多个阻塞线程被解除阻塞。FutureTask的release操作包括run()方法和cancel(…)方法
  2. FutureTask的源代码

    public class FutureTask<V> implements RunnableFuture<V> {
        private final Sync sync;
        
        public V get() throws InterruptedException, ExecutionException {
            return sync.innerGet();
        }
        
        public void run() {
            sync.innerRun();
        }
        
        public boolean cancel(boolean mayInterruptIfRunning) {
            return sync.innerCancel(mayInterruptIfRunning);
        }
        
        //内部类Sync
        private final class Sync extends AbstractQueuedSynchronizer {
            V innerGet(){...}
            void innerRun(){...}
            boolean innerCancel(boolean mayInterruptIfRunning){...}
            
            protected int tryAcquireShared(int ignore){...}
            protected boolean tryReleaseShared(int ignore){...}
            
        }
    }
    
  3. FutureTask的设计示意图
    这里写图片描述

  4. **FutureTask.get()**方法的执行过程

    1. 调用Sync.innerGet()
    2. 上述方法调用AQS.acquireSharedInterruptibly(int arg)
    3. 上述步骤2中的方法又调用Sync.tryAcquireShared()来判断acquire操作是否可以成功。
      • acquire操作可以成功的条件为:state为执行完成状态RAN或已取消状态CANCELLED,且runner不为null。
      • 如果成功,则get()方法立即返回;
      • 如果失败,则到线程等待队列中去等待其他线程执行release操作。
    4. 当其他线程执行release操作(如FutureTask.run()FutureTask.cancel(…))唤醒当前线程后,当前线程再次执行Sync.tryAcquireShared()将返回正值1,当前线程将离开线程等待队列并++唤醒它的后继线程++。
    5. 返回计算的结果或抛出异常。
  5. **FutureTask.run()**方法的执行过程

    1. 执行在构造函数中指定的任务(Callable.call())。
    2. 以原子方式来更新同步状态(调用AQS.compareAndSetState(int expect,int update),设置state为执行完成状态RAN)。
      • 如果这个原子操作成功,就设置代表计算结果的变量result的值为Callable.call()的返回值,然后调用AQS.releaseShared(int arg)
    3. AQS.releaseShared(int arg)首先会回调Sync.tryReleaseShared(arg)来执行release操作(设置运行任务的线程runner为null,然会返回true);然后唤醒线程等待队列中的第一个线程。
    4. 调用FutureTask.done()
  6. FutureTask的级联唤醒

    1. 场景:假设开始时FutureTask处于未启动状态或已启动状态,等待队列中已经有3个线程(A、B和C)在等待。
    2. 变化1:线程D执行get()方法。
      • 结果:线程D也到等待队列中去等待。
    3. 变化2:当线程E执行run()方法
      • 结果:唤醒队列中的第一个线程A。线程A被唤醒后,首先把自己从队列中删除,然后唤醒它的后继线程B,最后线程A从get()方法返回。线程B、C和D重复A线程的处理流程。最终,在队列中等待的所有线程都被级联唤醒并从get()方法返回

    这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值