ScheduledThreadPoolExecutor 介绍

  ScheduledThreadPoolExecutor 是一个可以实现定时任务的 ThreadPoolExecutor(线程池)。比 timer 更加灵活,效率更高!

  ScheduledThreadPoolExecutor结果如下图所示。

我们,ThreadPoolExecutor的execute和submit方法继承于AbstractExecutorService。而ScheduleExecutorService是一个接口,里面并没有execute和submit方法,ScheduleThreadPoolExecutor里面重写了execute和submit方法。

ScheduledThreadPoolExecutor的四个构造方法如下:

/**
* Creates a new {@code ScheduledThreadPoolExecutor} with the
* given core pool size.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
} /**
* Creates a new {@code ScheduledThreadPoolExecutor} with the
* given initial parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param threadFactory the factory to use when the executor
* creates a new thread
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @throws NullPointerException if {@code threadFactory} is null
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
} /**
* Creates a new ScheduledThreadPoolExecutor with the given
* initial parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @throws NullPointerException if {@code handler} is null
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), handler);
} /**
* Creates a new ScheduledThreadPoolExecutor with the given
* initial parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @throws NullPointerException if {@code threadFactory} or
* {@code handler} is null
*/
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory, handler);
}

  ScheduledThreadPoolExecutor 继承于 ThreadPoolExecutor ,从其构造方法可以看出,此线程池的线程也不会空闲超时(keepAliveTime = 0),同时使用队列是无边界的DelayedWorkQueue;要注意是,虽然此类继承自 ThreadPoolExecutor,但是有几个继承的调整方法对此类并无作用,特别是在此类中设置 maximumPoolSize 是没有意义的,因为ScheduleThreadPoolExecutor 使用了无边界的任务队列,所以根本不需要创建多于 corePoolsize 数量的线程。

ScheduleThreadPoolExecutor 主要的方法介绍

1. 零延时的 execute()、submit() 方法

   execute()、submit() 方法都被重写了,本质上调用的还是 schedule() 方法;从下面的源码可以看出,这两个方法提交的任务都是延时为0的 “实时任务”;

/**
* Executes {@code command} with zero required delay.
* This has effect equivalent to
* {@link #schedule(Runnable,long,TimeUnit) schedule(command, 0, anyUnit)}.
* Note that inspections of the queue and of the list returned by
* {@code shutdownNow} will access the zero-delayed
* {@link ScheduledFuture}, not the {@code command} itself.
*
* <p>A consequence of the use of {@code ScheduledFuture} objects is
* that {@link ThreadPoolExecutor#afterExecute afterExecute} is always
* called with a null second {@code Throwable} argument, even if the
* {@code command} terminated abruptly. Instead, the {@code Throwable}
* thrown by such a task can be obtained via {@link Future#get}.
*
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution because the
* executor has been shut down
* @throws NullPointerException {@inheritDoc}
*/
public void execute(Runnable command) {
schedule(command, 0, NANOSECONDS);
} // Override AbstractExecutorService methods /**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public Future<?> submit(Runnable task) {
return schedule(task, 0, NANOSECONDS);
}

2. 提交一个延时任务的 schedule() 方法

方法描述:

     /**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <V> ScheduledFuture<V> schedule(Callable<V> callable,
long delay,
TimeUnit unit) {
if (callable == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<V> t = decorateTask(callable,
new ScheduledFutureTask<V>(callable,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}

创建并执行在给定延迟后启用的 ScheduledFuture。

/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}

建并执行在给定延迟后启用的一次性操作。

3、 提交周期性的任务 scheduleAtFixedRate() 和 scheduleWithFixedDelay()

方法描述:

/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}

initialDelay 是此周期任务的开始执行时的延时时间(即只在第一次开始执行时延时,此后周期性地执行这个任务)。

/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
*/
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}

指定了首次执行前的初始延时时间,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。

两者的区别:

scheduleAtFixedRate: 固定的周期时间。此方法的 period 参数所指的间隔时间是 从上一周期的任务开始时间到当前周期的任务开始时间的间隔。当上一周期任务执行结束了,如果任务的执行时间大于 指定的周期时间period ,那么便可以开始此周期任务的下一周期的执行。否则,便是间隔时间还没有达到一周期的时间,还需要继续等待,直到周期时间到来;总的来说,可以分为以下两种情况:

  • 任务的执行时间 > period参数:那么周期运行的时间便是 任务的执行时间。
  • 任务的执行时间 < period参数:那么周期运行的时间便是 period参数。

scheduleWithFixedDelay: 固定的间隔时间。此方法的 delay 参数所指的间隔时间是 从上一周期的任务的执行结束时间到当前周期的任务开始时间的间隔,是指定任务的固定的运行间隔,与任务的执行时间无关。

@ Example1 scheduleAtFixedRate 测试

简单起见,下面创建了只有一个线程 ScheduledThreadPoolExecutor 对象,也只提交一个周期任务。 下面的例子中,任务的执行时间大于 period 参数。

public class ScheduledThreadPoolExecutorTest {

    public static void main(String[] args) {
//池中只有一个线程
ScheduledThreadPoolExecutor schedulePool = new ScheduledThreadPoolExecutor(1);
//作为一个周期任务提交,period 为1000ms,任务执行时间为2000ms
schedulePool.scheduleAtFixedRate(new MyRunnable(), 50, 1000, TimeUnit.MILLISECONDS);
} static class MyRunnable implements Runnable { int period = 1; @Override
public void run() {
//为周期任务捕获异常,避免异常影响下一周期的任务执行
try {
System.out.println("---------------第 " + period + " 周期-------------");
System.out.println("begin = " + System.currentTimeMillis() / 1000);//秒
//任务执行时间
Thread.sleep(2000);
System.out.println("end = " + System.currentTimeMillis() / 1000);
period++;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} }
}
}

运行结果:

---------------第 1 周期-------------
begin = 1563007610
end = 1563007612
---------------第 2 周期-------------
begin = 1563007612
end = 1563007614
---------------第 3 周期-------------
begin = 1563007614
end = 1563007616
---------------第 4 周期-------------
begin = 1563007616
end = 1563007618
---------------第 5 周期-------------
begin = 1563007618
end = 1563007620

从结果可以看出,任务的周期执行是连着的,没有间隔时间。这是因为任务的运行时间大于周期执行时间,即当任务还没结束时,周期时间已经到了,所以任务刚结束,就可以进行下一周期的执行。

@ Example2 scheduleWithFixedDelay 测试

同样也是上面的例子,将周期方法换成 scheduleWithFixedDelay( )

public static void main(String[] args) {
//池中只有一个线程
ScheduledThreadPoolExecutor schedulePool = new ScheduledThreadPoolExecutor(1);
//作为一个周期任务提交,delay 为1000ms
schedulePool.scheduleWithFixedDelay(new MyRunnable(), 50, 1000, TimeUnit.MILLISECONDS); }

运行结果:

---------------第 1 周期-------------
begin = 1563007901
end = 1563007903
---------------第 2 周期-------------
begin = 1563007904
end = 1563007906
---------------第 3 周期-------------
begin = 1563007907
end = 1563007909
---------------第 4 周期-------------
begin = 1563007910
end = 1563007912

上面的scheduleWithFixedDelay例子的任务是间隔一个固定的时间执行的,无论任务的执行时间是否大于周期时间。

4. 线程池关闭

两个关闭线程池的方法,一旦线程池被关闭,就会拒绝以后提交的所有任务:

void shutdown():

在以前已提交任务的执行中发起一个有序的关闭,但是不接受新任务。线程池中的周期任务、延时任务,根据下面的两个策略来判断是否继续正常运行,还是停止运行。

List<Runnable> shutdownNow():

尝试停止所有正在执行的任务、暂停等待任务的处理,并返回等待执行的任务列表。对于正在运行,尝试通过中断该线程来结束线程。对于尚未运行的任务,则都不再执行。

线程池关闭(shutdown())下的两个策略的描述

  • void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean value):
    在调用线程池调用了 shutdown()方法后,是否继续执行现有延时任务(就是通过 schedule()方法提交的延时任务 )的策略;默认值为false;在以下两种种的情况下,延时任务将会被终止:

  • void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean value)
    在调用线程池调用了 shutdown()方法后,是否继续执行现有周期任务(通过 scheduleAtFixedRate、scheduleWithFixedDelay 提交的周期任务)的策略;默认值为false;在以下两种的情况下,周期任务将会被终止:

获取这个两个策略的设置值:

boolean getContinueExistingPeriodicTasksAfterShutdownPolicy():

取有关在此执行程序已 shutdown 的情况下、是否继续执行现有定期任务的策略。

boolean getExecuteExistingDelayedTasksAfterShutdownPolicy():

获取有关在此执行程序已 shutdown 的情况下是否继续执行现有延迟任务的策略

@ Example3 shoutdown下的周期任务测试

还是基于上面的例子进行改造,main线程休眠10秒后,shutdown线程池。在默认的情况下(策略为false),因为间隔为1s,任务执行时间为2s,所以 shutdown 后,最多能执行4个周期;但是下面的例子,将策略的值设置为true,shutdown后,周期任务也可以正常运行下去。

public static void main(String[] args) throws InterruptedException{

        //池中只有一个线程
ScheduledThreadPoolExecutor schedulePool = new ScheduledThreadPoolExecutor(1);
//shutdown时,周期任务的策略
schedulePool.setContinueExistingPeriodicTasksAfterShutdownPolicy(true);
//作为周期任务提交
ScheduledFuture future = schedulePool.scheduleWithFixedDelay(new MyRunnable(), 50, 1000, TimeUnit.MILLISECONDS); Thread.sleep(10*1000); schedulePool.shutdown(); }

运行结果:

---------------第 1 周期-------------
begin = 1563008226
end = 1563008228
---------------第 2 周期-------------
begin = 1563008229
end = 1563008231
---------------第 3 周期-------------
begin = 1563008232
end = 1563008234
---------------第 4 周期-------------
begin = 1563008235
end = 1563008237
---------------第 5 周期-------------
begin = 1563008238
end = 1563008240
---------------第 6 周期-------------
begin = 1563008241
end = 1563008243
---------------第 7 周期-------------
begin = 1563008244

5. 移除任务、取消任务

BlockingQueue getQueue():
返回此执行程序使用的任务队列。此队列中的每个元素都是一个 ScheduledFuture,包括用 execute 所提交的那些任务
boolean remove(Runnable task):
从执行程序的内部队列中移除此任务(如果存在),从而如果尚未开始,则其不再运行。
void setRemoveOnCancelPolicy(boolean value):
此方法是在1.7引入的,是用于对调用cancel()的任务的处理策略:是否马上移除出队列;默认为false;
周期任务也可以通过 ScheduledFuture的 cancel()取消运行;

Executors 提供了两个常用的ScheduledThreadPoolExecutor

  这两个常用的ScheduledThreadPoolExecutor:SingleThreadScheduledExecutor(单线程的线程池)、ScheduledThreadPool(线程数量固定的线程池),下面是 Executors 对应的源代码。

/**
* Creates a single-threaded executor that can schedule commands
* to run after a given delay, or to execute periodically.
* (Note however that if this single
* thread terminates due to a failure during execution prior to
* shutdown, a new one will take its place if needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one task will be active at any
* given time. Unlike the otherwise equivalent
* {@code newScheduledThreadPool(1)} the returned executor is
* guaranteed not to be reconfigurable to use additional threads.
* @return the newly created scheduled executor
*/
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
} /**
* Creates a single-threaded executor that can schedule commands
* to run after a given delay, or to execute periodically. (Note
* however that if this single thread terminates due to a failure
* during execution prior to shutdown, a new one will take its
* place if needed to execute subsequent tasks.) Tasks are
* guaranteed to execute sequentially, and no more than one task
* will be active at any given time. Unlike the otherwise
* equivalent {@code newScheduledThreadPool(1, threadFactory)}
* the returned executor is guaranteed not to be reconfigurable to
* use additional threads.
* @param threadFactory the factory to use when creating new
* threads
* @return a newly created scheduled executor
* @throws NullPointerException if threadFactory is null
*/
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1, threadFactory));
} /**
* Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle
* @return a newly created scheduled thread pool
* @throws IllegalArgumentException if {@code corePoolSize < 0}
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
} /**
* Creates a thread pool that can schedule commands to run after a
* given delay, or to execute periodically.
* @param corePoolSize the number of threads to keep in the pool,
* even if they are idle
* @param threadFactory the factory to use when the executor
* creates a new thread
* @return a newly created scheduled thread pool
* @throws IllegalArgumentException if {@code corePoolSize < 0}
* @throws NullPointerException if threadFactory is null
*/
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

java多线程之ScheduleThreadPoolExecutor的更多相关文章

  1. Java多线程之ConcurrentSkipListMap深入分析(转)

    Java多线程之ConcurrentSkipListMap深入分析   一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...

  2. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  3. JAVA多线程之volatile 与 synchronized 的比较

    一,volatile关键字的可见性 要想理解volatile关键字,得先了解下JAVA的内存模型,Java内存模型的抽象示意图如下: 从图中可以看出: ①每个线程都有一个自己的本地内存空间--线程栈空 ...

  4. java多线程之yield,join,wait,sleep的区别

    Java多线程之yield,join,wait,sleep的区别 Java多线程中,经常会遇到yield,join,wait和sleep方法.容易混淆他们的功能及作用.自己仔细研究了下,他们主要的区别 ...

  5. Java多线程之Runnable与Thread

    Java多线程之Thread与Runnable 一.Thread VS Runnable 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类和 ...

  6. JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止

    JAVA多线程之UncaughtExceptionHandler——处理非正常的线程中止 背景 当单线程的程序发生一个未捕获的异常时我们可以采用try....catch进行异常的捕获,但是在多线程环境 ...

  7. java多线程之wait和notify协作,生产者和消费者

    这篇直接贴代码了 package cn.javaBase.study_thread1; class Source { public static int num = 0; //假设这是馒头的数量 } ...

  8. Java——多线程之Lock锁

    Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...

  9. java多线程之:深入JVM锁机制2-Lock (转载)

    前文(深入JVM锁机制-synchronized)分析了JVM中的synchronized实现,本文继续分析JVM中的另一种锁Lock的实现.与synchronized不同的是,Lock完全用Java ...

随机推荐

  1. what is the CCA?

    Clear Channel Assessment (CCA) is one of two carrier sense mechanisms in WLAN (or WiFi). It is defin ...

  2. pythonday06数据类型(四)

    今日内容 1.集合 2内存相关 1.集合set 无序 无重复 v = {1,2,3,4,5,6,99,100} # 疑问:v = {} """ None int v1 = ...

  3. Node.js爬虫实战 - 爬你喜欢的

    前言 今天没有什么前言,就是想分享些关于爬虫的技术,任性.来吧,各位客官,里边请... 开篇第一问:爬虫是什么嘞? 首先咱们说哈,爬虫不是"虫子",姑凉们不要害怕. 爬虫 - 一种 ...

  4. canvas 鼠标位置缩放图形

    最近再做 webcad , 需要在 canvas  上对图形进行缩放,主要分为以下几个步骤: 1.找到当前光标所在位置,确定其在相对 canvas 坐标系的坐标 绑定鼠标滚轮事件,假定每次缩放比例 0 ...

  5. SQL 分组后,获取每组中的最大值对应的数据

    select gr,num,dt,(select bys from test where gr=b.gr and dt=b.dt) bys from ( select gr,count(0) num, ...

  6. 纯数据结构Java实现(1/11)(动态数组)

    我怕说这部分内容太简单后,突然蹦出来一个大佬把我虐到哭,还是悠着点,踏实写 大致内容有: 增删改查,泛型支持,扩容支持,复杂度分析.(铺垫: Java语言中的数组) 基础铺垫 其实没啥好介绍的,顺序存 ...

  7. P3195 [HNOI2008]玩具装箱TOY 斜率优化dp

    传送门:https://www.luogu.org/problem/P3195 题目描述 P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任 ...

  8. MinorGC和FullGC的触发条件

    前言 无论是日常工作,还是企业面试,我们都会经常接触到GC.我们都知道GC是java中的垃圾回收策略.GC帮我们省去了很多事.在GC中,我经常听到的就属于MinorGC和FullGC了.那么在什么情况 ...

  9. PIXIJS的一些使用

    我发现pixijs在国内简直就是一片静土啊,只有那么一点点的微弱的不能再微弱的声音. 我在这里整理了下我使用过程中解决和可能理解的一些问题吧,都是一个个点,而不是完整的示例. 先放官网示例: http ...

  10. c#滑窗缓存

    前言 在大数据时代,软件系统需要具备处理海量数据的能力,同时也更加依赖于系统强大的存储能力与数据响应能力.各种大数据的工具如雨后春笋般孕育而生,这对于系统来说是极大的利好.但在后端采用分布式.云存储和 ...