Executors是一个线程池的工厂类,提供各种有用的线程池的创建,使用得当,将会使我们并发编程变得简单!今天就来聊聊这个工厂类的艺术吧!

  Executors只是Executor框架的主要成员组件之一,为java的异步任务调度执行提供了重要的入口!

  在说Executors之前,还需要说一下另一个Executor框架的重要成员,ThreadPoolExecutor。

  ThreadPoolExecutor 实现了ExecutorService接口,提供了线程池的调度功能!成为纯种池技术的基石!

ThreadPoolExecutor 的主要构造函数:

  1. /**
  2. * Creates a new {@code ThreadPoolExecutor} with the given initial
  3. * parameters.
  4. *
  5. * @param corePoolSize the number of threads to keep in the pool, even
  6. * if they are idle, unless {@code allowCoreThreadTimeOut} is set
  7. * @param maximumPoolSize the maximum number of threads to allow in the
  8. * pool
  9. * @param keepAliveTime when the number of threads is greater than
  10. * the core, this is the maximum time that excess idle threads
  11. * will wait for new tasks before terminating.
  12. * @param unit the time unit for the {@code keepAliveTime} argument
  13. * @param workQueue the queue to use for holding tasks before they are
  14. * executed. This queue will hold only the {@code Runnable}
  15. * tasks submitted by the {@code execute} method.
  16. * @param threadFactory the factory to use when the executor
  17. * creates a new thread
  18. * @param handler the handler to use when execution is blocked
  19. * because the thread bounds and queue capacities are reached
  20. * @throws IllegalArgumentException if one of the following holds:<br>
  21. * {@code corePoolSize < 0}<br>
  22. * {@code keepAliveTime < 0}<br>
  23. * {@code maximumPoolSize <= 0}<br>
  24. * {@code maximumPoolSize < corePoolSize}
  25. * @throws NullPointerException if {@code workQueue}
  26. * or {@code threadFactory} or {@code handler} is null
  27. */
  28. public ThreadPoolExecutor(int corePoolSize,
  29. int maximumPoolSize,
  30. long keepAliveTime,
  31. TimeUnit unit,
  32. BlockingQueue<Runnable> workQueue,
  33. ThreadFactory threadFactory,
  34. RejectedExecutionHandler handler) {
  35. if (corePoolSize < 0 ||
  36. maximumPoolSize <= 0 ||
  37. maximumPoolSize < corePoolSize ||
  38. keepAliveTime < 0)
  39. throw new IllegalArgumentException();
  40. if (workQueue == null || threadFactory == null || handler == null)
  41. throw new NullPointerException();
  42. this.corePoolSize = corePoolSize;
  43. this.maximumPoolSize = maximumPoolSize;
  44. this.workQueue = workQueue;
  45. this.keepAliveTime = unit.toNanos(keepAliveTime);
  46. this.threadFactory = threadFactory;
  47. this.handler = handler;
  48. }

  咱们此处不是要去分析其源码,只是想看一下怎样使用他!

  从构造方法可以看出 ThreadPoolExecutor 的主要参数 7 个,在其注释上也有说明功能,咱们翻译下每个参数的功能:

  1. corePoolSize: 线程池核心线程数(平时保留的线程数),使用时机: 在初始时刻,每次请求进来都会创建一个线程直到达到该size
  2. maximumPoolSize: 线程池最大线程数,使用时机: workQueue都放不下时,启动新线程,直到最大线程数,此时到达线程池的极限
  3. keepAliveTime/unit: 超出corePoolSize数量的线程的保留时间,unit为时间单位
  4. workQueue: 阻塞队列,当核心线程数达到或者超出后,会先尝试将任务放入该队列由各线程自行消费;
  5. ArrayBlockingQueue: 构造函数一定要传大小
  6. LinkedBlockingQueue: 构造函数不传大小会默认为65536Integer.MAX_VALUE ),当大量请求任务时,容易造成 内存耗尽。
  7. SynchronousQueue: 同步队列,一个没有存储空间的阻塞队列 ,将任务同步交付给工作线程。
  8. PriorityBlockingQueue: 优先队列
  9. threadFactory:线程工厂,用于线程需要创建时,调用其newThread()生产新线程使用
  10. handler: 饱和策略,当队列已放不下任务,且创建的线程已达到 maximum 后,则不能再处理任务,直接将任务交给饱和策略
  11. AbortPolicy: 直接抛弃(默认)
  12. CallerRunsPolicy: 用调用者的线程执行任务
  13. DiscardOldestPolicy: 抛弃队列中最久的任务
  14. DiscardPolicy: 抛弃当前任务

  所以,虽然参数有点多,但是其实仔细阅读以上的注释,基本就能很好地使用线程池了,不过咱们还可以通过一个流程图来说明这一切,对于喜欢看图说话的同学来说是件好事!

  好了,ThreadPoolExecutor 介绍完后,什么感觉呢? 说是简单,其实还是挺麻烦的,毕竟你还要去了解各种队列的公优缺点,去了解线程池范围怎样设置合理。所以,是时候让 Executors 工厂出场了!

  作为一个工厂类,Executors 简化了各种参数,只忘文生义即可明白其意思!Executors 主要提供4种类型的线程池!

1. FixedThreadPool 创建一个指定数量的线程池

  可控制线程最大并发数,超出的线程会在队列中等待。

  其实现方式如下:

  1. public static ExecutorService newFixedThreadPool(int nThreads) {
  2. return new ThreadPoolExecutor(nThreads, nThreads,
  3. 0L, TimeUnit.MILLISECONDS,
  4. new LinkedBlockingQueue<Runnable>());
  5. }

  使用固定线程数的worker和无界队列保存多余的task,所以创建的线程数不会超过corePoolSize,所以maxPoolSize是一个无效参数,所以keepAliveTime是一个无效参数,所以不会调用RejectedExecutionHandler.rejectedExecution()方法,即无拒绝策略可用;从而在外部表现来看,就是固定线程,在执行无限的队列!

2. SingleThreadExecutor 创建一个单线程化的线程池

  它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

  其实现方式如下:

  1. public static ExecutorService newSingleThreadExecutor() {
  2. return new FinalizableDelegatedExecutorService
  3. (new ThreadPoolExecutor(1, 1,
  4. 0L, TimeUnit.MILLISECONDS,
  5. new LinkedBlockingQueue<Runnable>()));
  6. }

  与 FixedThreadPool 原理类似,不过这里只会使用一个线程在运行。使用一个线程可以保证取任务时的顺序一致性,从而表现出先到先得的效果!在要求有执行顺序要求的场景,刚好派上用场!

3. CachedThreadPool, 创建一个可缓存的无限线程池

  如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

  其实现方式如下:

  1. public static ExecutorService newCachedThreadPool() {
  2. return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
  3. 60L, TimeUnit.SECONDS,
  4. new SynchronousQueue<Runnable>());
  5. }

  使用SyncronousQueue作为线程池的工作队列,这是特殊的队列,它是个没容量的阻塞队列(至于为什么要用这个特性,据说是性能比 LinkedBlockingQueue 更高,详解他),每个插入操作必须有一个线程对应的移除操作,反之一样;当提交一个cached任务时,执行SyncronousQueue.offer(x),如果有等待的空闲线程,则匹配成功,exec立即返回;如果没有匹配,则会创建一个新线程执行任务;任务执行完后使用poll()等待keep即60秒,一直尝试从队列中获取任务,超时后自动释放线程;

  使用这种线程池把握好节奏,因为看起来线程池动不动就会创建系列线程池,而且动不动就会释放线程,完全是不可控的,不可监控的。

4. ScheduledThreadPool 创建一个定长线程池

  支持定时及周期性任务执行。

  其实现如下:

  1. public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
  2. return new ScheduledThreadPoolExecutor(corePoolSize);
  3. }

  看起来,这个在参数方面没有太多的解说,只是依赖于 ScheduledThreadPoolExecutor 的实现了!所以,我们来看一下 ScheduledThreadPoolExecutor是怎么实现的?

  1. public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor {}
  2. public ScheduledThreadPoolExecutor(int corePoolSize) {
  3. super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
  4. new DelayedWorkQueue());
  5. }

  ok, 如上两个,就够了!ScheduledThreadPoolExecutor 也是继承了 ThreadPoolExecutor, 其构造方法也是依赖于父类,只是将 队列实现改为 DelayedWorkQueue,从而实现延时运行或者定期执行的效果!

  而相比于 Timer 来说,ScheduledThreadPool会有更好的执行效果,因为Timer只会有一个线程来执行任务,会有局限性。而且 ScheduledThreadPool 提供其他很多的可操作方法!

  Executors 的出现,使线程池的使用难度大大降低,使开发更方便,在合适的场合使用相应的工厂方法,定能让开发事半功倍!

线程池工厂Executors编程的艺术的更多相关文章

  1. java笔记--使用线程池优化多线程编程

    使用线程池优化多线程编程 认识线程池 在Java中,所有的对象都是需要通过new操作符来创建的,如果创建大量短生命周期的对象,将会使得整个程序的性能非常的低下.这种时候就需要用到了池的技术,比如数据库 ...

  2. 线程池之 Executors

    线程池之 Executors + 面试题 线程池的创建分为两种方式:ThreadPoolExecutor 和 Executors,上一节学习了 ThreadPoolExecutor 的使用方式,本节重 ...

  3. 线程池——JAVA并发编程指南

    TPS00-J. 用线程池实现应用在流量暴涨时优雅降级 很多程序都要解决这样一个问题——处理一系列外来的请求.Thread- Per-Message这种设计模式是最简单的并发策略了,它为每一个请求创建 ...

  4. Java线程池ThreadPoolExecutor&&Executors

    一.先看看传统的开启线程. new Thread(new Runnable() { @Override public void run() { } }).start(); 缺点: 1.每次new Th ...

  5. Java并发包线程池之Executors、ExecutorCompletionService工具类

    前言 前面介绍了Java并发包提供的三种线程池,它们用处各不相同,接下来介绍一些工具类,对这三种线程池的使用. Executors Executors是JDK1.5就开始存在是一个线程池工具类,它定义 ...

  6. 线程池工厂方法newFixedThreadPool()和newCachedThreadPool()

    newFixedThreadPool()方法: 该方法返回一个固定数量的线程池,当一个新的任务提交时,线程池中若有空闲线程,则立即执行. 若没有.则新的任务被暂存在一个任务队列中,待线程空闲时,便处理 ...

  7. ThreadFactory 线程池工厂

    import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Thr ...

  8. 线程池(2)-Executors提供4个线程池

    1.为什么不使用Executors提供4个线程池创建线程池 阿里巴巴开放手册这样写: . [强制]线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式, ...

  9. 线程池工厂方法newScheduledThreadPool(),计划任务

    package com.thread.test.ThreadPool; import java.util.concurrent.Executors; import java.util.concurre ...

随机推荐

  1. Linux screenshot

    一.简介 linux下的三个截图软件

  2. win10jdk环境变量配置问题:'javac' 不是内部或外部命令,也不是可运行的程序 或批处理文件。

    在编译时报错:'javac' 不是内部或外部命令,也不是可运行的程序 或批处理文件. 原因1:配置Path的时候使用%JAVA_HOME%相对路径配置. 解决:把Path路径改为绝对路径(例:D:\P ...

  3. 第一二次java实训作业

    1. 声明一个整型变量a,并赋初值5,在程序中判断a是奇数还是偶数,然后输出判断的结果. package java1; public class java1 { static int a=5; pub ...

  4. Finance版本计划

    2.0.0.3 1. 支持自定义报表 提供页面自定义报表,根据存储过程,前端页面传参调用 根据存储过程数据集控制简单样式(数字靠右,列宽) 2. 支持报表公式可修改 定义公式规则(文档) 2.0.0. ...

  5. AX_CreateAndPostPurch

    static void CreateAndPostPurch(Args _args) { List il = new List(Types::Record); DocumentNum Document ...

  6. AX_SysTableBrowser

    sysTableBrowser sysTableBrowser = new sysTableBrowser();  ;  sysTableBrowser.setAllowEdit(true);  sy ...

  7. Mybatis中的CDATA标签

    术语 CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data). 在 XML 元素中,"<" 和 "&& ...

  8. ORM创建多表以及多表的增删改查

    一. 多表的创建 1. 一对一 在哪个表中设置都行,但是添加数据的时候需要现在没有外键的表中添加数据 models.OneToOneField(to="表名",to_field=& ...

  9. 第51章:Java操作MongoDB-[Mongo-Java-2.x]

    ①范例:连接数据库 package cn.mldn.demo; import com.mongodb.DB; import com.mongodb.MongoClient; public class ...

  10. Scrum的3种角色划分--2048小游戏

    WBS分析: 敏捷开发 Scrum的3种角色划分:产品负责人PO(Product Owner).SM(ScrumMaster).开发团队(Team) 职责: PO:负责在限定期限内拟定可能的最有价值的 ...