面试:讲一讲Java并发-Executor?

📖摘要


今天分享下 —— 面试:讲一讲Java并发-Executor? 的一些基本知识,欢迎关注!


🌂分享:Executor框架

在这里插入图片描述


😀Executor接口
public interface Executor {
     void execute(Runnable command);
 }

Executor 接口是 Executor 框架中最基础的部分,定义了一个用于执行Runnableexecute 方法,它没有实现类只有另一个重要的子接口 ExecutorService


😁ExecutorService接口
//继承自Executor接口
  public interface ExecutorService extends Executor {
      /**
       * 关闭方法,调用后执行之前提交的任务,不再接受新的任务
       */
      void shutdown();
      /**
       * 从语义上可以看出是立即停止的意思,将暂停所有等待处理的任务并返回这些任务的列表
       */
     List<Runnable> shutdownNow();
     /**
      * 判断执行器是否已经关闭
      */
     boolean isShutdown();
     /**
      * 关闭后所有任务是否都已完成
      */
     boolean isTerminated();
     /**
      * 中断
      */
    boolean awaitTermination(long timeout, TimeUnit unit)
         throws InterruptedException;
     /**
      * 提交一个Callable任务
      */
     <T> Future<T> submit(Callable<T> task);
     /**
      * 提交一个Runable任务,result要返回的结果
      */
     <T> Future<T> submit(Runnable task, T result);
     /**
      * 提交一个任务
      */
     Future<?> submit(Runnable task);
     /**
      * 执行所有给定的任务,当所有任务完成,返回保持任务状态和结果的Future列表
      */
     <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
         throws InterruptedException;
     /**
      * 执行给定的任务,当所有任务完成或超时期满时(无论哪个首先发生),返回保持任务状态和结果的 Future 列表。
      */
     <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 接口,定义了终止、提交、执行任务、跟踪任务返回结果等方法。

ExecutorService 的生命周期有三种状态:运行、关闭和已终止。

  • execute(Runnable command):履行 Ruannable 类型的任务,
  • submit(task):可用来提交 CallableRunnable 任务,并返回代表此任务的 Future 对象
  • shutdown():在完成已提交的任务后封闭办事,不再接管新任务,
  • shutdownNow():停止所有正在履行的任务并封闭办事。
  • isTerminated():测试是否所有任务都履行完毕了。,
  • isShutdown():测试是否该 ExecutorService 已被关闭

😂工具类 Executors 的静态方法

负责生成各种类型的 ExecutorService 线程池实例,共有四种。

  • newFixedThreadPool(numberOfThreads:int):(固定线程池)ExecutorService 创建一个固定线程数量的线程池,并行执行的线程数量不变,线程当前任务完成后,可以被重用执行另一个任务
  • newCachedThreadPool():(可缓存线程池) ExecutorService 创建一个线程池,按需创建新线程,如果线程的当前规模超过了处理需求时,阿么将回收空闲的线程,而当需求增加时,则可以添加新的线程,线程池的规模不存在任何限制
  • new SingleThreadExecutor();(单线程执行器)线程池中只有一个线程,依次执行任务
  • new ScheduledThreadPool():线程池按时间计划来执行任务,允许用户设定执行任务的时间,类似于 timer

🤣Runnable、Callable、Future接口

Runnable接口


// 实现Runnable接口的类将被Thread执行,表示一个基本的任务
  public interface Runnable {
      // run方法就是它所有的内容,就是实际执行的任务
      public abstract void run();
  }

Callable接口


Runnable 接口的区别在于它接收泛型,同时它执行任务后带有返回内容

// Callable同样是任务,与Runnable接口的区别在于它接收泛型,同时它执行任务后带有返回内容
  public interface Callable<V> {
      // 相对于run方法的带有返回值的call方法
      V call() throws Exception;
}

Runnable 接口和 Callable 接口的实现类,都可以被 ThreadPoolExecutorScheduledThreadPoolExecutor 执行,他们之间的区别是 Runnable 不会返回结果,而 Callable 可以返回结果。

Executors 可以把一个 Runnable 对象转换成 Callable 对象:

public static Callable<Object> callable(Runnbale task);

当把一个 Callable 对象 (Callable1,Callable2) 提交给 ThreadPoolExecutorScheduledThreadPoolExecutor 执行时,submit(...) 会向我们返回一个 FutureTask 对象。我们执行 FutureTask.get() 来等待任务执行完成,当任务完成后, FutureTask.get() 将返回任务的结果。

Future接口


// Future代表异步任务的执行结果
  public interface Future<V> {
      /**
       * 尝试取消一个任务,如果这个任务不能被取消(通常是因为已经执行完了),返回false,否则返回true。
       */
      boolean cancel(boolean mayInterruptIfRunning);
      /**
      * 返回代表的任务是否在完成之前被取消了
      */
     boolean isCancelled();
     /**
      * 如果任务已经完成,返回true
      */
    boolean isDone();
     /**
      * 获取异步任务的执行结果(如果任务没执行完将等待)
      */
    V get() throws InterruptedException, ExecutionException;
     /**
      * 获取异步任务的执行结果(有最常等待时间的限制)
      *
      *  timeout表示等待的时间,unit是它时间单位
      */
     V get(long timeout, TimeUnit unit)
         throws InterruptedException, ExecutionException, TimeoutException;
 }

Future 就是对于具体的 Runnable 或者 Callable 任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过 get 方法获取执行结果,该方法会阻塞直到任务返回结果

  • cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。
  • isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
  • isDone方法表示任务是否已经完成,若任务完成,则返回true;
  • get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
  • get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

也就是说Future提供了三种功能:

  • 判断任务是否完成;
  • 能够中断任务;
  • 能够获取任务执行结果。

线程池的处理流程


在这里插入图片描述

线程池优先要创建出基本线程池大小(corePoolSize)的线程数量,没有达到这个数量时,每次提交新任务都会直接创建一个新线程,当达到了基本线程数量后,又有新任务到达,优先放入等待队列,如果队列满了,才去创建新的线程(不能超过线程池的最大数maxmumPoolSize)。


😃向线程池提交任务的两种方式

通过execute()方法


ExecutorService threadpool= Executors.newFixedThreadPool(10);  threadpool.execute(new Runnable(){...});

这种方式提交没有返回值,也就不能判断任务是否被线程池执行成功。

通过submit()方法


Future<?> future = threadpool.submit(new Runnable(){...});
    try {
            Object res = future.get();//获取任务执行结果
        } catch (InterruptedException e) {
            // 处理中断异常
            e.printStackTrace();
        } catch (ExecutionException e) {
            // 处理无法执行任务异常
            e.printStackTrace();
        }finally{
            // 关闭线程池
            executor.shutdown();
        }

使用 submit 方法来提交任务,它会返回一个 Future 对象,通过 futureget 方法来获取返回值,get 方法会阻塞住直到任务完成,而使用 get(long timeout, TimeUnit unit) 方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完。

线程池本身的状态


volatile int runState;
static final int RUNNING = 0;   //运行状态
static final int SHUTDOWN = 1;   //关闭状态
static final int STOP = 2;       //停止
static final int TERMINATED = 3; //终止,终结
  • 当创建线程池后,初始时,线程池处于 RUNNING 状态;
  • 如果调用了 shutdown() 方法,则线程池处于 SHUTDOWN 状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕,最后终止;
  • 如果调用了 shutdownNow() 方法,则线程池处于 STOP 状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务,返回没有执行的任务列表;
  • 当线程池处于 SHUTDOWNSTOP 状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为 TERMINATED 状态。

😄ForkJoinPool

ForkJoinPool 是jdk1.7新引入的线程池,基于 ForkJoin 框架,使用了“分治”的思想。关于ForkJoin框架参考另一篇笔记“Java并发之J.U.C”。

ThreadPoolExecutor 中每个任务都是由单个线程独立处理的,如果出现一个非常耗时的大任务(比如大数组排序),就可能出现线程池中只有一个线程在处理这个大任务,而其他线程却空闲着,这会导致CPU负载不均衡:空闲的处理器无法帮助工作繁忙的处理器。

ForkJoinPool 就是用来解决这种问题的:将一个大任务拆分成多个小任务后,使用 fork 可以将小任务分发给其他线程同时处理,使用 join 可以将多个线程处理的结果进行汇总;这实际上就是分治思想的并行版本


😅ForkJoinPool 基本原理

ForkJoinPool 类是 Fork/Join 框架的核心,和 ThreadPoolExecutor 一样它也是 ExecutorService 接口的实现类。

所以上面的线程池框架图是比较老的了,新的框架图如下图所示:

在这里插入图片描述


🎉最后

  • 更多参考精彩博文请看这里:《陈永佳的博客》

  • 喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈永佳

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值