Executor框架简介

Java的线程既是工作单元,也是执行机制。从JDK5开始,把工作单元和执行机制分离开来。

Executor框架由3大部分组成

  • 任务。

    • 被执行任务需要实现的接口:Runnable接口或Callable接口

    • 异步计算的结果。Future接口和FutureTask类。

  • 任务的执行。两个关键类ThreadPoolExecutor和SeheduledThreadPoolExecutor。

任务

Runnable接口

不含有运行结果

  1. public interface Runnable {
  2. public abstract void run();
  3. } 

Callable接口

含有运行结果

  1. public interface Callable<V> {
  2. V call() throws Exception;
  3. }

Runnable与Callable的区别

Runnable和Callable的区别是,
(1)Callable规定的方法是call(),Runnable规定的方法是run().

(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的

(3)call方法可以抛出异常,run方法不可以

(4)运行Callable任务可以拿到一个Future对象,Future 表示异步计算的结果。

Future接口

Future接口代表异步计算的结果,通过Future接口提供的方法可以查看异步计算是否执行完成,或者等待执行结果并获取执行结果,同时还可以取消执行。

  1. public interface Future<V> {
  2. boolean cancel(boolean mayInterruptIfRunning);//取消任务
  3. boolean isCancelled();//是否取消了
  4. boolean isDone();//任务是否完成
  5. //获取任务执行结果,如果任务还没完成则会阻塞等待直到任务执行完成
  6. V get() throws InterruptedException, ExecutionException;
  7. //等待一段时间尝试获取执行结果,
  8. V get(long timeout, TimeUnit unit)
  9. throws InterruptedException, ExecutionException, TimeoutException;
  10. }

一个使用Runnable的简单例子

  1. import java.util.concurrent.*;
  2.  
  3. public class Test {
  4. public static void main(String[] args) throws InterruptedException, ExecutionException {
  5. final ExecutorService exec = Executors.newFixedThreadPool(5);
  6. Runnable task = new Runnable() {
  7. public void run() {
  8. try {
  9. System.out.println(Thread.currentThread().getName() + " is running");
  10. Thread.sleep(1000 * 10);//休眠指定的时间,此处表示该操作比较耗时
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. };
  16. exec.submit(task);
  17. //关闭线程池
  18. exec.shutdown();
  19. }
  20. }

一个使用Callable的简单例子

  1. import java.util.concurrent.*;
  2.  
  3. public class Test {
  4. public static void main(String[] args) throws InterruptedException, ExecutionException {
  5. final ExecutorService exec = Executors.newFixedThreadPool(5);
  6. Callable<String> call = new Callable<String>() {
  7. public String call() throws Exception {
  8. Thread.sleep(1000 * 10);//休眠指定的时间,此处表示该操作比较耗时
  9. return "Other less important but longtime things.";
  10. }
  11. };
  12. Future<String> task = exec.submit(call);
  13. //重要的事情
  14. System.out.println("Let's do important things. start");
  15. Thread.sleep(1000 * 3);
  16. System.out.println("Let's do important things. end");
  17.  
  18. //不重要的事情
  19. while(!task.isDone()){
  20. System.out.println("still waiting....");
  21. Thread.sleep(1000 * 1);
  22. }
  23. System.out.println("get sth....");
  24. String obj = task.get();
  25. System.out.println(obj);
  26. //关闭线程池
  27. exec.shutdown();
  28. }
  29. }

运行结果

  1. Let's do important things. start
  2. Let's do important things. end
  3. still waiting....
  4. still waiting....
  5. still waiting....
  6. still waiting....
  7. still waiting....
  8. still waiting....
  9. still waiting....
  10. get sth....
  11. Other less important but longtime things.

任务的执行机制

ThreadPoolExecutor

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

核心配置参数包括

corePoolSize:核心线程池的大小

maximumPoolSize:最大线程池的大小

BlockingQueue:暂时保存任务的工作队列

RejectedExecutionHandler:当ThreadPoolExecutor已经饱和时(达到了最大线程池大小且工作队列已满)将执行的Handler。

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue) {
  6. this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
  7. Executors.defaultThreadFactory(), defaultHandler);
  8. }

可以创建3种类型的ThreadPoolExecutor。

  • FixedThreadPool

  • SingleThreadExecutor

  • CachedThreadPool

1.FixedThreadPool

FixedThreadPool是固定线程数的线程池,最多线程池中有nThreads个线程。

  1. public static ExecutorService newFixedThreadPool(int nThreads) {
  2. return new ThreadPoolExecutor(nThreads, nThreads, //nThreads为固定线程数
  3. 0L, TimeUnit.MILLISECONDS, //空闲线程的等待时间为0ms,表示立刻被终止
  4. new LinkedBlockingQueue<Runnable>()); //工作队列
  5. }

FixedThreadPool的execute()方法内部执行过程

  1. 当新任务被提交时,如果当前运行线程数小于nTheads,创建新线程执行任务

  2. 如果当前运行线程数等于设置的最大线程数nThreads,将新任务加入到工作队列LinkedBlockingQueue中

  3. 线程执行完任务后会反复从LinkedBlockingQueue中获取新任务执行

  4. LinkedBlockingQueue中没有新任务,线程空闲,线程将被终止。

注意点:

由于工作队列使用的是无界队列LinkedBlockingQueue,FixedThreadPool不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution()方法)。

2.SingleThreadExecutor

SingleThreadExecutor是FixedThreadPool的特例,线程池中线程的固定数量为1,即最多有一个线程。

  1. public static ExecutorService newSingleThreadExecutor() {
  2. return new FinalizableDelegatedExecutorService
  3. (new ThreadPoolExecutor(1, 1, //nThreads为固定线程数
  4. 0L, TimeUnit.MILLISECONDS, //空闲线程的等待时间为0ms,表示立刻被终止
  5. new LinkedBlockingQueue<Runnable>())); //工作队列
  6. }

SingleThreadExecutor的execute()方法内部执行过程与注意事项可参考FixedThreadPool的。

3.CachedThreadPool

CachedThreadPool是根据需要创建新线程的线程池。

  1. public static ExecutorService newCachedThreadPool() {
  2. return new ThreadPoolExecutor(0, Integer.MAX_VALUE, //coolPoolSize为0,maxinumPoolSize为Integer.MAX_VALUE
  3. 60L, TimeUnit.SECONDS, //空闲线程的等待时间,空闲60s后被终止
  4. new SynchronousQueue<Runnable>()); //工作队列
  5. }
 

CachedThreadPool的execute()方法的内部运行过程

  1. 当新任务被提交时,主线程将任务插入到工作队列中(SynchronousQueue的offer()方法),如果线程池有空闲线程在等待任务,新任务交给空闲线程处理。

  2. 如果线程池中没有在等待任务的空闲线程,创建新线程执行任务

  3. 线程执行完任务后,等待60s(SynchronousQueue.poll(60, TimeUnit.SECONDS)方法),如果没有等待新任务,线程终止

SynchronousQueue是一个没有容量的BlockingQueue。每一个插入操作必须等待另一个线程的移除操作。

CachedThreadPool使用SynchronousQueue,把主线程提交的任务传递给空闲线程。

注意点:

CachedThreadPool的线程池是无解的,没有限制数量,如果主线程提交任务的速度高于线程处理任务的速度,将不断创建新线程。

极端情况下,会因创建过多线程耗尽CPU和内存。

ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor主要用于定时任务(定期执行,给定延迟后执行)

执行方式

1.scheduleAtFixedRate

任务按照固定周期执行,比如设定每10分钟执行一次,第8分钟时候执行了第一次,后续执行时间点为第18分钟,第28分钟,第38分钟

  1. public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, //任务
  2. long initialDelay, //初始延迟
  3. long period, //任务执行周期
  4. TimeUnit unit)  

2.scheduleWithFixedDelay

任务按照固定延迟执行,比如设定延迟时间为10是分钟,第8分钟时候执行了第一次,任务执行完成后,再等待10分钟,执行下一次。如果任务执行了2分钟,则下一次为第20分钟

  1. public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,//任务
  2. long initialDelay, //初始延迟
  3. long delay, //任务执行延迟
  4. TimeUnit unit)

实现原理

数据结构

ScheduledThreadPoolExecutor:定时任务执行器

DelayQueue:使用DelayQueue作为任务队列,保存待调度的任务,任务按照执行的时间点排序。DelayQueue内部是用PriorityQueue实现。

ScheduledFutureTask:待调度任务。

ScheduledFutureTask的成员变量:

  • time:任务将被执行的具体时间

  • sequenceNumber:任务序号,time相同时,序号小的先执行

  • period:任务执行的间隔周期

运行机制

ScheduledThreadPoolExecutor内部运行过程

  1. 线程从DelayQueue中获取到到期的ScheduledFutureTask,到期是指time大于等于当前时间。

  2. 执行任务ScheduledFutureTask

  3. 修改ScheduledFutureTask的time为下次要执行的时间,放回到DelayQueue中

【Java并发】Executor框架的更多相关文章

  1. 构建自己的Java并发模型框架

    Java的多线程特性为构建高性能的应用提供了极大的方便,可是也带来了不少的麻烦.线程间同步.数据一致性等烦琐的问题须要细心的考虑,一不小心就会出现一些微妙的,难以调试的错误. 另外.应用逻辑和线程逻辑 ...

  2. 构建Java并发模型框架

    Java的多线程特性为构建高性能的应用提供了极大的方便,但是也带来了不少的麻烦.线程间同步.数据一致性等烦琐的问题需要细心的考虑,一不小心就会出现一些微妙的,难以调试的错误.另外,应用逻辑和线程逻辑纠 ...

  3. Java并发模型框架

    构建Java并发模型框架 Java的多线程特性为构建高性能的应用提供了极大的方便,但是也带来了不少的麻烦.线程间同步.数据一致性等烦琐的问题需要细心的考虑,一不小心就会出现一些微妙的,难以调试的错误. ...

  4. Java的Executor框架和线程池实现原理

    Java的Executor框架 1,Executor接口 public interface Executor { void execute(Runnable command); } Executor接 ...

  5. JAVA的Executor框架

    Executor框架分离了任务的创建和执行.JAVA SE5的java.util.concurrent包中的执行器(Executor)管理Thread对象,从而简化了并发编程.Executor引入了一 ...

  6. Java并发基础框架AbstractQueuedSynchronizer初探(ReentrantLock的实现分析)

    AbstractQueuedSynchronizer是实现Java并发类库的一个基础框架,Java中的各种锁(RenentrantLock, ReentrantReadWriteLock)以及同步工具 ...

  7. java并发编程框架 Executor ExecutorService invokeall

    首先介绍两个重要的接口,Executor和ExecutorService,定义如下: public interface Executor { void execute(Runnable command ...

  8. JAVA并发-Executor

    结构 类继承图: 上面的各个接口/类的关系和作用: Executor 执行器接口,也是最顶层的抽象核心接口, 分离了任务和任务的执行. ExecutorService 在Executor的基础上提供了 ...

  9. Java并发包下锁学习第二篇Java并发基础框架-队列同步器介绍

    Java并发包下锁学习第二篇队列同步器 还记得在第一篇文章中,讲到的locks包下的类结果图吗?如下图: ​ 从图中,我们可以看到AbstractQueuedSynchronizer这个类很重要(在本 ...

  10. Java的Executor框架和线程池实现原理(转)

    ExecutorService接口继承自Executor接口,定义了终止.提交,执行任务.跟踪任务返回结果等方法 1,execute(Runnable command):履行Ruannable类型的任 ...

随机推荐

  1. mysql数据库如何设置表名大小写不敏感?

    转自:https://blog.csdn.net/iefreer/article/details/8313839 在跨平台的程序设计中要注意到mysql的一些系统变量在windows和linux上的缺 ...

  2. jCarousel.js 插件

    轮播图 中间放大 内容跟着切换 (参考 米趣 网站) html   <div class="jcarousel-wrapper"> <div class=&quo ...

  3. JavaScript-烂笔头

    JavaScript 对大小写敏感 注释单行用:// 注释多汗用:/* */ 声明变量:var 变量名 (未使用值来声明的变量,值为undefined) JavaScript 变量均为对象 可以使用关 ...

  4. debian各种包

    1 zlib compression library sudo apt-get install zlib1g-dev 2 c-ares库 libc-ares-dev - asynchronous na ...

  5. Backtracking is a form of recursion.

    w https://www.cis.upenn.edu/~matuszek/cit594-2012/Pages/backtracking.html Starting at Root, your opt ...

  6. HTML布局四剑客-Flex,Grid,Table,Float

    前言 在HTML布局中有很多的选择,同一种表现方式可以使用不同的方法来实现.下面来对四种最常见的布局方式进行阐述和解释,它们分别是Float,Table,Grid和Flex Float 第一位出场的就 ...

  7. Druid对数据库密码加密的坑

    背景: 在对已有项目搭建本地环境,修改了本地ip端口和数据库帐号密码(使用了明文). 然后项目一直跑不起来,还抛出各种异常,经过分析发现主要错在这里:druid java.lang.IllegalAr ...

  8. php中get_cfg_var()和ini_get()的用法及区别

    php里get_cfg_var()和ini_get()都是取得配置值的函数,当你需要获取php.ini里的某个选项的配置值时,这两个函数都都可以使用,得到的结果是一样的. 不过,get_cfg_var ...

  9. CNI portmap插件实现源码分析

    DNAT创建的iptables规则如下:(重写目的IP和端口) PREROUTING, OUTPUT: --dst-type local -j CNI-HOSTPORT_DNAT  // PREROU ...

  10. 我的Android进阶之旅------>Android关于HttpsURLConnection一个忽略Https证书是否正确的Https请求工具类

    下面是一个Android HttpsURLConnection忽略Https证书是否正确的Https请求工具类,不需要验证服务器端证书是否正确,也不需要验证服务器证书中的域名是否有效. (PS:建议下 ...