当前位置:   article > 正文

Java ScheduledExecutorService 使用学习

java scheduledexecutorservice

        Java 中的 ScheduledExecutorService 接口用来实现延迟执行或者定时执行的任务。在阅读 RocketMQ 源码(release-4.7.1版本)的过程中,发现很多地方都是使用的 ScheduledExecutorService 来实现定时任务。比如,在 broker 启动过程,BrokerController 类中使用 scheduledExecutorService 执行 broker 启动后的定时任务,比如消息消费偏移量 offset 的持久化定时任务,大致如下:

  1. package org.apache.rocketmq.broker;
  2. public class BrokerController {
  3. /**
  4. * 这里省略一些字段
  5. */
  6. /**
  7. * 使用 Executors.newSingleThreadScheduledExecutor 创建单线程的定时调度任务线程池
  8. */
  9. private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
  10. "BrokerControllerScheduledThread"));
  11. /**
  12. * broker 启动的初始化方法
  13. */
  14. public boolean initialize() throws CloneNotSupportedException {
  15. // 省略部分代码
  16. this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  17. @Override
  18. public void run() {
  19. try {
  20. BrokerController.this.getBrokerStats().record();
  21. } catch (Throwable e) {
  22. log.error("schedule record error.", e);
  23. }
  24. }
  25. }, initialDelay, period, TimeUnit.MILLISECONDS);
  26. // broker 启动10s后,默认每隔5s钟持久化一次消费消息偏移量offset信息
  27. this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  28. @Override
  29. public void run() {
  30. try {
  31. // 持久化消费偏移量,将 ConsumerOffsetManager 序列化为json文件保存在本地
  32. BrokerController.this.consumerOffsetManager.persist();
  33. } catch (Throwable e) {
  34. log.error("schedule persist consumerOffset error.", e);
  35. }
  36. }
  37. }, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS);
  38. this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  39. @Override
  40. public void run() {
  41. try {
  42. BrokerController.this.consumerFilterManager.persist();
  43. } catch (Throwable e) {
  44. log.error("schedule persist consumer filter error.", e);
  45. }
  46. }
  47. }, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS);
  48. // 后面还有很多.......
  49. }
  50. }

所以觉得有必要学习理解 ScheduledExecutorService。大致看了一下 ScheduledExecutorService 实现类 ScheduledThreadPoolExecutor 的代码,跟我们常用的 ThreadPoolExecutor 线程池类还是有一些差异的,里面有一个基于堆实现的优先队列,后面要再深入学习下,本篇先学习 ScheduledExecutorService 的使用,先学会使用,再分析原理。

本文使用JDK版本:JDK8.

一、ScheduledExecutorService 接口定义

首先,ScheduledExecutorService 是一个接口,它实现了 ExecutorService 接口。

在 ScheduledExecutorService 接口中又新定义了下面四个方法:

1、public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

在指定延迟时间后执行一个 Runnable 任务。因为是 Runnable ,返回值 ScheduledFuture.get() 返回值为 null。

2、 <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);

在指定延迟时间后执行一个 Callable 任务。因为是 Callable ,所以 ScheduledFuture.get() 有返回值。

3、ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);

创建并执行一个在给定初始延迟 (initialDelay) 后首次启动执行的定时任务,任务执行具有给定的周期 (period);
也就是将在 initialDelay 后开始执行,然后在 initialDelay + period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。
如果任务的任何一个执行遇到异常,则后续执行都会被取消相反,任务正常执行的话,只能通过线程池的取消或终止操作来终止该任务。

如果此任务的任何一个执行要花费比其周期更长的时间,则后续的执行将会被推迟,不会出现两个任务同时执行。

4、ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

创建并执行一个在给定初始延迟 (initialDelay) 后首次启动执行的任务,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟(delay)。
如果任务的任一执行遇到异常,就会取消后续任务执行。相反,任务正常执行的话,只能通过线程池的取消或终止操作来终止该任务。

二、ScheduledExecutorService 接口使用

Executors 类中封装了几个方法返回 ScheduledExecutorService 的实现:

  1. public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
  2. return new DelegatedScheduledExecutorService
  3. (new ScheduledThreadPoolExecutor(1));
  4. }
  5. public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
  6. return new DelegatedScheduledExecutorService
  7. (new ScheduledThreadPoolExecutor(1, threadFactory));
  8. }
  9. public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
  10. return new ScheduledThreadPoolExecutor(corePoolSize);
  11. }
  12. public static ScheduledExecutorService newScheduledThreadPool(
  13. int corePoolSize, ThreadFactory threadFactory) {
  14. return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
  15. }
  16. public static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor) {
  17. if (executor == null)
  18. throw new NullPointerException();
  19. return new DelegatedScheduledExecutorService(executor);
  20. }

可以看到底层实现主要借助两个类: DelegatedScheduledExecutorServiceScheduledThreadPoolExecutor。其中 newSingle- 开头的方法返回的线程池实现只有一个核心线程,也就用这一个线程执行提交的延迟或者定时任务。我们也使用 rocketmq 中用的 Executors.newSingleThreadScheduledExecutor(ThreadFactory threadFactory):

Executors.newSingleThreadScheduledExecutor(ThreadFactory threadFactory)

  1. public class ScheduledExecutorServiceTest {
  2. public static void main(String[] args) {
  3. ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
  4. "TestScheduledThread"));
  5. scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  6. @Override
  7. public void run() {
  8. Thread thread = Thread.currentThread();
  9. System.out.println("我是间隔1s执行的任务,线程name:"+ thread.getName() + ",执行时间戳:" + System.currentTimeMillis() / 1000);
  10. }
  11. }, 0L, 1, TimeUnit.SECONDS);
  12. scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  13. @Override
  14. public void run() {
  15. Thread thread = Thread.currentThread();
  16. System.out.println("我是间隔5s执行的任务,线程name:"+ thread.getName() + ",执行时间戳:" + System.currentTimeMillis() / 1000);
  17. }
  18. }, 3L, 5, TimeUnit.SECONDS);//延迟 3s 后再执行定时任务
  19. }
  20. }
  21. class ThreadFactoryImpl implements ThreadFactory {
  22. private final AtomicLong threadIndex = new AtomicLong(0);
  23. private final String threadNamePrefix;
  24. private final boolean daemon;
  25. public ThreadFactoryImpl(final String threadNamePrefix) {
  26. this(threadNamePrefix, false);
  27. }
  28. public ThreadFactoryImpl(final String threadNamePrefix, boolean daemon) {
  29. this.threadNamePrefix = threadNamePrefix;
  30. this.daemon = daemon;
  31. }
  32. @Override
  33. public Thread newThread(Runnable r) {
  34. Thread thread = new Thread(r, threadNamePrefix + this.threadIndex.incrementAndGet());
  35. thread.setDaemon(daemon);
  36. return thread;
  37. }
  38. }

scheduledExecutorService.scheduleAtFixedRate 方法

注意:我们上面采用的是 scheduledExecutorService.scheduleAtFixedRate 方法。

运行 main 方法一段时间后,结束运行,观察控制台日志:

  1. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374865
  2. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374866
  3. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374867
  4. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374868
  5. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374868
  6. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374869
  7. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374870
  8. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374871
  9. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374872
  10. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374873
  11. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374873
  12. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374874
  13. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374875
  14. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374876
  15. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374877
  16. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374878
  17. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374878
  18. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617374879

可以看到 1s 中的任务每一秒执行一次,5s 任务首次执行在延迟 3s 钟之后,每 5s 执行一次。

scheduledExecutorService.scheduleAtFixedRate 方法如果某个定时任务某次执行异常了,那么该任务后续将不会再执行,我们测试一下:

  1. scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  2. @Override
  3. public void run() {
  4. Thread thread = Thread.currentThread();
  5. System.out.println("我是间隔5s执行的任务,线程name:"+ thread.getName() + ",执行时间戳:" + System.currentTimeMillis() / 1000);
  6. //这里抛出异常
  7. throw new RuntimeException();
  8. }
  9. }, 3L, 5, TimeUnit.SECONDS);

控制台输出:

  1. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375180
  2. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375181
  3. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375182
  4. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375183
  5. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375183
  6. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375184
  7. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375185
  8. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375186
  9. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375187
  10. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375188
  11. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375189
  12. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375190
  13. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375191
  14. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375192
  15. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375193
  16. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375194
  17. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375195

可以看出,5s 的任务只执行了一次,因为抛出了异常便不再执行了。 1s 的任务则不受影响。所以我们最好要像 rocketmq 中那样使用,用 try - catch 包裹住任务执行代码,并且打出 error 级别的日志:

  1. scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  2. @Override
  3. public void run() {
  4. try{
  5. Thread thread = Thread.currentThread();
  6. System.out.println("我是间隔5s执行的任务,线程name:"+ thread.getName() + ",执行时间戳:" + System.currentTimeMillis() / 1000);
  7. throw new RuntimeException();
  8. } catch (Exception e){
  9. System.out.println(e);//模拟输出 error 信息
  10. }
  11. }
  12. }, 3L, 5, TimeUnit.SECONDS);

修改之后,控制台输出:

  1. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375547
  2. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375548
  3. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375549
  4. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375550
  5. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375550
  6. java.lang.RuntimeException
  7. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375551
  8. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375552
  9. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375553
  10. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375554
  11. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375555
  12. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375555
  13. java.lang.RuntimeException
  14. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375556
  15. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375557
  16. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375558
  17. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375559

5s 的任务虽然第一次抛出了异常,但是 catch 处理异常后,后续任务仍会正常执行。

再测试一下,如果任务执行时间超出了定时周期的情况:

  1. public static void main(String[] args) {
  2. ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
  3. "TestScheduledThread"));
  4. scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  5. @Override
  6. public void run() {
  7. Thread thread = Thread.currentThread();
  8. System.out.println("我是间隔1s执行的任务,线程name:"+ thread.getName() + ",执行时间戳:" + System.currentTimeMillis() / 1000);
  9. try {
  10. // 模拟任务执行耗时5s
  11. Thread.sleep(5000);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. }, 0L, 1, TimeUnit.SECONDS);
  17. }

控制台输出结果:

  1. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375850
  2. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375855
  3. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375860
  4. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375865
  5. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375870
  6. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617375875

可以看出:scheduledExecutorService.scheduleAtFixedRate 方法如果定时任务执行时间超过了定时周期时长,则后续的任务执行将会被推迟,不会出现两个任务同时执行。如果在这种情况下,我们再把上面首次延迟 3s 执行的 5s 的定时任务加上,会有什么现象:

  1. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376360
  2. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376365
  3. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376370
  4. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376375
  5. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376380
  6. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376380
  7. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376385
  8. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376390
  9. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376395
  10. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376400
  11. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376405
  12. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376405
  13. 我是间隔1s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617376410

可以看出,因为 1s 定时任务执行时间超过了执行周期,影响到了 5s 定时任务的周期执行,控制台显示 5s 的任务隔了 25 秒才执行,并且首次延迟的时间也非常不准确了,这是因为我们使用的是Executors.newSingleThreadScheduledExecutor(ThreadFactory threadFactory) ,它底层只有一个线程处理这些定时任务,所以导致这种问题的发生。如果我们改为如下设置多个核心线程的ScheduledExecutorService 实现就可以避免这个问题:

  1. //使用两个核心线程
  2. ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);

控制台输出结果,1s 和 5s 两个定时任务执行都恢复正常了:

  1. 我是间隔1s执行的任务,线程name:pool-1-thread-1,执行时间戳:1617377444
  2. 我是间隔5s执行的任务,线程name:pool-1-thread-2,执行时间戳:1617377447
  3. 我是间隔1s执行的任务,线程name:pool-1-thread-1,执行时间戳:1617377449
  4. 我是间隔5s执行的任务,线程name:pool-1-thread-2,执行时间戳:1617377452
  5. 我是间隔1s执行的任务,线程name:pool-1-thread-1,执行时间戳:1617377454
  6. 我是间隔5s执行的任务,线程name:pool-1-thread-2,执行时间戳:1617377457
  7. 我是间隔1s执行的任务,线程name:pool-1-thread-1,执行时间戳:1617377459
  8. 我是间隔5s执行的任务,线程name:pool-1-thread-2,执行时间戳:1617377462
  9. 我是间隔1s执行的任务,线程name:pool-1-thread-1,执行时间戳:1617377464

scheduledExecutorService.scheduleWithFixedDelay 方法

这个方法每次执行任务是有指定的时间间隔:

  1. public static void main(String[] args) {
  2. ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
  3. "TestScheduledThread"));
  4. scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
  5. @Override
  6. public void run() {
  7. Thread thread = Thread.currentThread();
  8. System.out.println("我是每次执行完毕间隔2s执行的任务,线程name:"+ thread.getName() + ",开始执行时间戳:" + System.currentTimeMillis() / 1000);
  9. // 随机数模拟每次执行耗时不同
  10. int random = new Random().nextInt(6);
  11. try {
  12. Thread.sleep(random * 1000);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. System.out.println("我是每次执行完毕间隔2s执行的任务,线程name:"+ thread.getName() + ",执行完毕时间戳:" + System.currentTimeMillis() / 1000);
  17. }
  18. }, 0L, 2, TimeUnit.SECONDS);
  19. }

控制台输出,每次执行完毕后隔 2s 开启下一次执行:

  1. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,开始执行时间戳:1617378188
  2. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,执行完毕时间戳:1617378193
  3. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,开始执行时间戳:1617378195
  4. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,执行完毕时间戳:1617378199
  5. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,开始执行时间戳:1617378201
  6. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,执行完毕时间戳:1617378206
  7. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,开始执行时间戳:1617378208
  8. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,执行完毕时间戳:1617378210
  9. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,开始执行时间戳:1617378212
  10. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,执行完毕时间戳:1617378214
  11. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,开始执行时间戳:1617378216
  12. 我是每次执行完毕间隔2s执行的任务,线程name:TestScheduledThread1,执行完毕时间戳:1617378217

ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)

这个方法需要注意的是,ScheduledFuture 继承了 Future 接口,scheduledFuture.get() 也会阻塞调用方线程直至取到结果。

  1. public static void main(String[] args) throws ExecutionException, InterruptedException {
  2. ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
  3. "TestScheduledThread"));
  4. ScheduledFuture scheduledFuture = scheduledExecutorService.schedule(new Callable() {
  5. @Override
  6. public Integer call() {
  7. Thread thread = Thread.currentThread();
  8. System.out.println("我是延迟10s执行的一次性任务,线程name:"+ thread.getName() + ",执行时间戳:" + System.currentTimeMillis() / 1000);
  9. try {
  10. Thread.sleep(5000);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. return 250;
  15. }
  16. }, 10L, TimeUnit.SECONDS);
  17. // scheduledFuture.get() 会阻塞调用方线程,这里是 main 线程
  18. System.out.println("scheduledFuture.get():" + scheduledFuture.get());
  19. System.out.println("hello world");
  20. }

方法混合在一起使用

把上面几种方法混合在一起调用:

  1. public static void main(String[] args) throws ExecutionException, InterruptedException {
  2. ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
  3. "TestScheduledThread"));
  4. scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
  5. @Override
  6. public void run() {
  7. Thread thread = Thread.currentThread();
  8. System.out.println("我是间隔5s执行的任务,线程name:"+ thread.getName() + ",执行时间戳:" + System.currentTimeMillis() / 1000);
  9. }
  10. }, 0L, 5, TimeUnit.SECONDS);
  11. ScheduledFuture scheduledFuture = scheduledExecutorService.schedule(new Callable() {
  12. @Override
  13. public Integer call() {
  14. Thread thread = Thread.currentThread();
  15. System.out.println("我是延迟10s执行的一次性任务,线程name:"+ thread.getName() + ",执行时间戳:" + System.currentTimeMillis() / 1000);
  16. try {
  17. Thread.sleep(5000);
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. return 250;
  22. }
  23. }, 10L, TimeUnit.SECONDS);
  24. // scheduledFuture.get() 会阻塞调用方线程,这里是 main 线程
  25. System.out.println("scheduledFuture.get():" + scheduledFuture.get());
  26. System.out.println("hello world");
  27. }

输出结果:

  1. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617379143
  2. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617379148
  3. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617379153
  4. 我是延迟10s执行的一次性任务,线程name:TestScheduledThread1,执行时间戳:1617379153
  5. scheduledFuture.get():250
  6. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617379158
  7. hello world
  8. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617379163
  9. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617379168
  10. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617379173
  11. 我是间隔5s执行的任务,线程name:TestScheduledThread1,执行时间戳:1617379178

运行非常好~

感觉 ScheduledExecutorService 很强大,后续要分析一下上述两个类 DelegatedScheduledExecutorServiceScheduledThreadPoolExecutor 的底层实现。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/83809
推荐阅读
相关标签
  

闽ICP备14008679号