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. jenkins 构建一个maven项目

    1.首先在 全局工具配置 里配置maven的路径信息 这里因为之前已经下载了maven并放在了E盘,因此只需要在 MAVEN_HOME 添加maven文件夹的路径 如若本地还没maven,勾选 “自动 ...

  2. Linux查看进程,端口,访问url

    # 查看进程# ps -ef|grep python# 终止进程# kill -9 id # 端口 netstat -ntl # 显示正在监听的tcp端口,以端口号显示 netstat -apn|gr ...

  3. C宏定义

    宏定义中宏名一般用大写,用以和一般的变量名区分开来,但是用宏名用小写也没有错; 对程序中用双引号括起来的字符串内的字符,不进行宏替换操作, #include<stdio.h> #defin ...

  4. Apache Maven 入门篇

    2017-11-09注释:IntelliJ IDEA 2017.2.5 x64 等新版本会安装maven,为了有好的体验 建议在安装目录找到IntelliJ IDEA 2017.2.5\plugins ...

  5. quartz 定时任务的增删改

    参考:  https://blog.csdn.net/llmys/article/details/81069863

  6. as3 根据鼠标移动方向

    gz 为一个影片 import flash.display.Sprite; stage.addEventListener(MouseEvent.MOUSE_MOVE, onStageMouseMove ...

  7. Linux下类似windows下_beginthread和_endthread 的多线程开发

    在 windows下头文件中包含 #include<process.h> 就可以使用_beginthread进行线程创建.个人感觉挺方便的. 在linux下类似于_beginthread ...

  8. ABAP-BarCode-1-HTML二维码打印及动态屏幕实现

    很久很久...以前写的一个测试程序,主要是通过ABAP与HTML交互,编写JavaScript实现二维码及动态屏幕实现. 1.ABAP界面效果 2.实现代码 *&--------------- ...

  9. js实现UTC时间转为北京时间,时间戳转为时间

    用了阿里云的接口,发现其穿的日期是UTC格式的.需要转换. var utc_datetime = "2017-03-31T08:02:06Z"; function utc2beij ...

  10. apache commons pool

    apache commons下的pool 其中的borrowObject函数源代码显示其产生可用对象的过程: 如果stack中有空闲的对象,则pop对象,激活对象(activate函数),验证对象(v ...