Executor(二)ThreadPoolExecutor、ScheduledThreadPoolExecutor 及 Executors 工厂类

Java 中的线程池类有两个,分别是:ThreadPoolExecutor 和 ScheduledThreadPoolExecutor,这两个类都继承自 ExecutorService。利用这两个类,可以创建各种不同的Java线程池,为了方便我们创建线程池,Java API提供了 Executors 工厂类来帮助我们创建各种各样的线程池。下面我们分别介绍一下这三个类。

一、ThreadPoolExecutor

public ThreadPoolExecutor(
int corePoolSize, // 核心线程数,初始化的线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程最大存活时间
TimeUnit unit, // 单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, //
RejectedExecutionHandler handler // 拒绝任务时的操作(拒绝策略)
) {}

自定义线程池有两个重要的参数:BlockingQueue 和 RejectedExecutionHandler

  • BlockingQueue 任务队列:

    1. 使用有界队列时,有新的任务需要执行时:

      a. 若线程池实际线程数小于 corePoolSize ,则优先创建线程;

      b. 若大于 corePoolSize 则会将任务加入队列;

      c. 若队列已满,则在总线程数不大于 maximumPoolSize 的前提下,创建新的线程;

      d. 若线程数大于 maximumPoolSize 则执行拒绝策略。

    2. 使用无界队列时,与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新任务到来时:

      a. 若线程数小于 corePoolSize 时,则创建新的线程;

      b. 若等于 corePoolSize 则将任务加入队列中,但就不会继续增加线程,也就是说,使用有界队列时,线程池的最大线程数为 corePoolSize;

      c. 若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存为至。

  • RejectedExecutionHandler JDK拒绝策略:

    1. AbortPolicy 该策略直接抛出异常,阻止系统工作

    2. CallerRunsPolicy 只要线程池未关闭,该策略直接在调用者线程中运行当前被丢弃的任务。显然这样不会真的丢弃任务,但是,调用者线程性能可能急剧下降。

    3. DiscardOledestPolicy 丢弃最老的一个请求任务,也就是丢弃一个即将被执行的任务,并尝试再次提交当前任务。

    4. DiscardPolicy 默默的丢弃无法处理的任务,不予任何处理。

    5. RejectedExecutioHandler 自定义拒绝策略,接口如下:

      public interfaceRejectedExecutionHandler{
      voidrejectedExecution(Runnable r,ThreadPoolExecutor executor);
      }

自定义线程池例子:

public class CustomedThreadPoolTest implements Runnable {
private int id;
private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); public CustomedThreadPoolTest(int id) {
this.id = id;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
;
}
System.out.println(format.format(new Date()) + " " + Thread.currentThread().getName() +
":任务-" + this.id);
} public static void main(String[] args) throws InterruptedException {
LinkedBlockingQueue queue = new LinkedBlockingQueue(); // (1)
//ArrayBlockingQueue queue = new ArrayBlockingQueue(5); // (2)
ThreadPoolExecutor customPool = new ThreadPoolExecutor(
2, // 核心线程
5, // 最大线程,无界队列时无用
5, // 线程最大空闲时间
TimeUnit.SECONDS,
queue, // 任务缓存
new RejectedExecutionHandler() { // (3)
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("拒绝:任务-" + ((CustomedThreadPoolTest) r).getId());
}
}
); for (int i = 1; i <= 10; i++) {
customPool.execute(new CustomedThreadPoolTest(i));
} Thread.sleep(1000);
System.out.println("剩余的任务数量为:" + queue.size());
customPool.shutdown();
}
}
  1. 使用无界队列 LinkedBlockingQueue 时,

    • 第一步:创建 corePoolSize=2 个线程分别执行"任务-1"、"任务-2";

    • 第二步:其余任务全部加入到 queue 队列中,若有空闲的线程则依次取出任务执行, 有界队列的最大线程数是 corePoolSize

    剩余的任务数量为:10
    2017-12-09 03:51:32 pool-1-thread-2:任务-2
    2017-12-09 03:51:32 pool-1-thread-1:任务-1
    2017-12-09 03:51:33 pool-1-thread-1:任务-4
    2017-12-09 03:51:33 pool-1-thread-2:任务-3
    2017-12-09 03:51:34 pool-1-thread-1:任务-5
    2017-12-09 03:51:34 pool-1-thread-2:任务-6
    2017-12-09 03:51:35 pool-1-thread-1:任务-7
    2017-12-09 03:51:35 pool-1-thread-2:任务-8
    2017-12-09 03:51:36 pool-1-thread-1:任务-9
    2017-12-09 03:51:36 pool-1-thread-2:任务-10
    2017-12-09 03:51:37 pool-1-thread-1:任务-11
    2017-12-09 03:51:37 pool-1-thread-2:任务-12
  2. 使用有界队列 ArrayBlockingQueue 时,

    • 第一步:先创建 corePoolSize=2 个线程执行"任务-1"-"任务-2";

    • 第二步:将其余的任务全部加入到 queue 中,直到 queue 被装满(5个 任务3-任务7);

    • 第三步:创建新的线程执行剩余的任务,直到达到 maximumPoolSize=5 ,即执行"任务8"-"任务10";

    • 第四步:对其余的任务执行拒绝策略,"任务-11"-"任务-12";

    • 第五步:从队列中依次取出线程执行。

    拒绝:任务-11
    拒绝:任务-12
    剩余的任务数量为:5
    2017-12-09 03:46:32 pool-1-thread-2:任务-2
    2017-12-09 03:46:32 pool-1-thread-3:任务-8
    2017-12-09 03:46:32 pool-1-thread-4:任务-9
    2017-12-09 03:46:32 pool-1-thread-5:任务-10
    2017-12-09 03:46:32 pool-1-thread-1:任务-1
    2017-12-09 03:46:33 pool-1-thread-2:任务-3
    2017-12-09 03:46:33 pool-1-thread-3:任务-4
    2017-12-09 03:46:33 pool-1-thread-4:任务-5
    2017-12-09 03:46:33 pool-1-thread-5:任务-6
    2017-12-09 03:46:33 pool-1-thread-1:任务-7
  3. 自定义拒绝策略 rejectedExecution

    new RejectedExecutionHandler() { // (3)
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
    // 记录日志,空闲再做处理
    System.out.println("拒绝:任务-" + ((CustomedThreadPoolTest) r).getId());
    }
    }

二、ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor 是 ExecutorService 的另一个实现类,从上面 Java 线程池 ExecutorService 继承树这幅图可以看出,ScheduledThreadPoolExecutor 直接继承自 ScheduledExecutorService,ScheduledThreadPoolExecutor 类的功能也主要体现在 ScheduledExecutorService 接口上,而所以在介绍 ScheduledThreadPoolExecutor 之前先介绍一下 ScheduledExecutorService 接口。

2.1 ScheduledExecutorService 接口介绍

public interface ScheduledExecutorService extends ExecutorService {

    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) schedule(Runnable command, long delay, TimeUnit unit)

这个方法的意思是在指定延迟之后运行 task。这个方法有个问题,就是没有办法获知 task 的执行结果。如果我们想获得 task 的执行结果,我们可以传入一个 Callable 的实例。

(2) schedule(Callable callable, long delay, TimeUnit unit)

这个方法与 schedule(Runnable task) 类似,也是在指定延迟之后运行 task,不过它接收的是一个 Callable 实例,此方法会返回一个 ScheduleFuture 对象,通过 ScheduleFuture 我们可以取消一个未执行的 task,也可以获得这个 task 的执行结果。

(3) scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

这个方法的作用是周期性的调度 task 执行。task 第一次执行的延迟根据 initialDelay 参数确定,以后每一次执行都间隔 period 时长。

如果 task 的执行时间大于定义的 period,那么下一个线程将在当前线程完成之后再执行。整个调度保证不会出现一个以上任务同时执行。

(4) scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

scheduleWithFixedDelay 的参数和 scheduleAtFixedRate 参数完全一致,它们的不同之处在于对 period 调度周期的解释。

在 scheduleAtFixedRate 中,period 指的两个任务开始执行的时间间隔,也就是当前任务的开始执行时间和下个任务的开始执行时间之间的间隔。

而在 scheduleWithFixedDelay 中,period 指的当前任务的结束执行时间到下个任务的开始执行时间。

2.2 ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor,构造参数很简单,只有 3 个:

public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}
  1. int corePoolSize:线程池维护线程的最少数量
  2. ThreadFactory threadFactory:线程工程类,线程池用它来制造线程
  3. RejectedExecutionHandler handler:线程池对拒绝任务的处理策略

三、Executors

创建一个什么样的 ExecutorService 的实例(即线程池)需要我们的具体应用场景而定,不过 Java 给我们提供了一个 Executors 工厂类,它可以帮助我们很方便的创建各种类型 ExecutorService 线程池,Executors 一共可以创建下面这四类线程池:

(1) newFixedThreadPool

newFixedThreadPool 创建一个固定数量的线程池,可控制线程最大并发数,超出的线程会在队列中等待。

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

(2) newSingleThreadExecutor

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

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

(3) newCachedThreadPool

newCachedThreadPool 创建一个可根据实际情况调整线程个数的线程池,不限制最大线程个数。当有任务提交时,用空闲线程执行任务,没有则创建一个线程,并且每个空闲线程会在60秒后自动回收。

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

(4) newScheduledThreadPool

newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}

备注:Executors 只是一个工厂类,它所有的方法返回的都是 ThreadPoolExecutor、ScheduledThreadPoolExecutor 这两个类的实例。


每天用心记录一点点。内容也许不重要,但习惯很重要!

Executor(二)ThreadPoolExecutor、ScheduledThreadPoolExecutor 及 Executors 工厂类的更多相关文章

  1. 【Java 多线程】Java线程池类ThreadPoolExecutor、ScheduledThreadPoolExecutor及Executors工厂类

    Java中的线程池类有两个,分别是:ThreadPoolExecutor和ScheduledThreadPoolExecutor,这两个类都继承自ExecutorService.利用这两个类,可以创建 ...

  2. Executor框架(五)Executors工厂类

    Executors 简介 Executors 是一个工厂类,其提供的是Executor.ExecutorService.ScheduledExecutorService.ThreadFactory 和 ...

  3. 聊聊高并发(四十四)解析java.util.concurrent各个组件(二十) Executors工厂类

    Executor框架为了更方便使用,提供了Executors这个工厂类.通过一系列的静态工厂方法.能够高速地创建对应的Executor实例. 仅仅有一个nThreads參数的newFixedThrea ...

  4. Executors工厂类

    newCachedThreadPool 重用之前的线程 适合执行许多短期异步任务的程序. 调用 execute() 将重用以前构造的线程 如果没有可用的线程,则创建一个新线程并添加到池中 默认为60s ...

  5. 【Java并发核心四】Executor 与 ThreadPoolExecutor

    Executor 和 ThreadPoolExecutor 实现的是线程池,主要作用是支持高并发的访问处理. Executor 是一个接口,与线程池有关的大部分类都实现了此接口. ExecutorSe ...

  6. 多线程编程(六)-Executor与ThreadPoolExecutor的使用

    使用Executors工厂类创建线程池 1.使用newCachedThreadPool()方法创建无界线程池 newCachedThreadPool()方法创建的是无界线程池,可以进行线程自动回收,此 ...

  7. 并发编程从零开始(十四)-Executors工具类

    并发编程从零开始(十四)-Executors工具类 12 Executors工具类 concurrent包提供了Executors工具类,利用它可以创建各种不同类型的线程池 12.1 四种对比 单线程 ...

  8. java工厂类与反射机制

    java 简单工厂类 2012-04-22 15:44:07|  分类: java |  标签:java工厂类  简单工厂类  |举报|字号 订阅     简单工厂模式需要由以下角色组成: 接口    ...

  9. 工厂类分离与java反射机制

    网易 博客 发现 小组 风格 手机博客 玩LOFTER,免费冲印20张照片!> 创建博客登录  加关注 黙言-在路上 奋斗 首页 日志 相册 音乐 收藏 博友 关于我             黙 ...

随机推荐

  1. PyQt5系列教程(八)定时器QTimer的使用

    软硬件环境 OS X EI Capitan Python 3.5.1 PyQt 5.5.1 前言 如果需要在程序中周期性地进行某项操作,比如检测某种设备的状态,就会用到定时器.本文就来看看PyQT5中 ...

  2. lb集群lvs的3种模式

    Cluster原理 集群的总类: 1.负载均衡集群(LB:Load Banlancing):实现将一个访问量或者任务量特别大的应用,给他 平均分配到不同的服务器上面,以提供高容量.大并发. 2.高可用 ...

  3. centos7.3安装zend guard loader3.3 for php5.6

    1 下载zend guard loader 到这里选择自己的系统版本  我选择的64位 for php5.6.3  linux http://www.zend.com/en/products/load ...

  4. c++builder Delphi 直接使用剪贴板 Clipboard

    c++builder Delphi 直接使用剪贴板 Clipboard 剪贴板 delphi use  Vcl.Clipbrd procedure TForm27.FormCreate(Sender: ...

  5. 13 python 常用的内置方法介绍

    1.isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(object) ...

  6. ABAP-动态程序生成

    科技越来越进步,人也就变的越来越懒,最终的演变就是大脑发达,四肢退化...AI的到来,准备接招吧... 报表若没有过多的用户交互逻辑,一般可通过SQ01配置生成,本文介绍用ABAP方式实现报表程序的动 ...

  7. windows10配置java开发环境

    一.下载jdk 二.安装jdk路径,这个路径不能包含中文名 三.系统会提示安装jre,安装目录不要是jdk的安装目录,否则会覆盖掉jdk目录下的jre目录 四. .;%JAVA_HOME%\lib;% ...

  8. Mysql日期时间Extract函数介绍

    MySQL日期时间Extract函数的优点在于可以选取日期时间的各个部分,从年一直到微秒,让我们对MySQL日期时间的处理更为轻松. MySQL 日期时间 Extract(选取)函数.1. 选取日期时 ...

  9. <c:forEach>取得集合数量

    这个问题曾经也困扰了我好久,不过以后都没有用过也都忘记了,不过今天在做项目 的时候又遇到了,花费了很久的时间都没有成功.也试了它的很多属性,例如:varStatus.last.${status.cou ...

  10. html file 文件批量上传 以及碰到的一些问题提

    //javascript 代码 $("#submite").click(function (evt) { var arrayTr = $("#datatables&quo ...