线程执行者(八)执行者周期性地运行一个任务

简介:

执行者周期性地运行一个任务

执行者框架提供ThreadPoolExecutor类,使用池中的线程执行并发任务,从而避免所有线程的创建操作。当你提交任务给执行者,根据它的配置,它尽快地执行任务。当它结束,任务将被执行者删除,如果你想再次运行任务,你必须再次提交任务给执行者。

但是执行者框架通过ScheduledThreadPoolExecutor类可以执行周期性任务。在这个指南中,你将学习如何通过使用这个类的功能来安排一个周期性任务。

准备工作…

这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。

如何做…

按以下步骤来实现的这个例子:

1.创建Task类,并指定它实现Runnable接口。


1 public class Task implements Runnable {

2.声明一个私有的、类型为String、名为name的属性,用来存储任务的名称。


1 private String name;

3.实现Task类的构造器,初始化name属性。


1 public Task(String name) {
2 this.name=name;
3 }

4.实现run()方法,写入实际日期到控制台,检查任务在指定的时间内执行。


1 @Override
2 public String call() throws Exception {
3 System.out.printf("%s: Starting at : %s\n",name,new Date());
4 return "Hello, world";
5 }

5.实现示例的主类,创建Main类,实现main()方法。


1 public class Main {
2 public static void main(String[] args) {

6.使用Executors类的newScheduledThreadPool()方法,创建ScheduledThreadPoolExecutor。传入参数1给这个方法。


1 ScheduledExecutorService executor=Executors.newScheduledThreadPool(1);

7.写入实际日期到控制台。


1 System.out.printf("Main: Starting at: %s\n",new Date());

8.创建一个新的Task对象。


1 Task task=new Task("Task");

9.使用scheduledAtFixRate()方法把它提交给执行者。使用前面创建的任务,数字1,数字2和常量TimeUnit.SECONDS作为参数。这个方法返回ScheduledFuture对象,它可以用来控制任务的状态。


1 ScheduledFuture<?> result=executor.scheduleAtFixedRate(task,1, 2, TimeUnit.SECONDS);

10.创建10个循环步骤,写入任务下次执行的剩余时间。在循环中,使用ScheduledFuture对象的getDelay()方法,获取任务下次执行的毫秒数。


01 for (int i=0; i<10; i++){
02 System.out.printf("Main: Delay: %d\n",result.
03 getDelay(TimeUnit.MILLISECONDS));
04 //线程睡眠500毫秒
05 try {
06 TimeUnit.MILLISECONDS.sleep(500);
07 } catch (InterruptedException e) {
08 e.printStackTrace();
09 }
10 }

11.使用shutdown()方法关闭执行者。


1 executor.shutdown();

12.使线程睡眠5秒,检查周期性任务是否完成。


1 try {
2 TimeUnit.SECONDS.sleep(5);
3 } catch (InterruptedException e) {
4 e.printStackTrace();
5 }

13.写入一条信息到控制台,表明程序结束。


1 System.out.printf("Main: Finished at: %s\n",new Date());

它是如何工作的…

当你想要使用执行者框架执行一个周期性任务,你需要ScheduledExecutorService对象。Java建议使用 Executors类创建执行者,Executors类是一个执行者对象工厂。在本例中,你应该使用newScheduledThreadPool()方法,创建一个 ScheduledExecutorService对象。这个方法接收池的线程数作为参数。正如在本例中你只有一个任务,你传入了值1作为参数。

一旦你有执行者需要执行一个周期性任务,你提交任务给该执行者。你已经使用了scheduledAtFixedRate()方法。此方法接收4个参数:你想要周期性执行的任务、第一次执行任务的延迟时间、两次执行之间的间隔期间、第2、3个参数的时间单位。它是TimeUnit类的常 量,TimeUnit类是个枚举类,有如下常量:DAYS,HOURS,MICROSECONDS, MILLISECONDS, MINUTES,,NANOSECONDS 和SECONDS。

很重要的一点需要考虑的是两次执行之间的(间隔)期间,是这两个执行开始之间的一段时间。如果你有一个花5秒执行的周期性任务,而你给一段3秒时间,同一时刻,你将会有两个任务在执行。

scheduleAtFixedRate() 方法返回ScheduledFuture对象,它继承Future接口,这个方法和调度任务一起协同工作。ScheduledFuture是一个参数化接口(校对注:ScheduledFuture<V>)。在这个示例中,由于你的任务是非参数化的Runnable对象,你必须使用 问号作为参数。

你已经使用ScheduledFuture接口的一个方法。getDelay()方法返回直到任务的下次执行时间。这个方法接收一个TimeUnit常量,这是你想要接收结果的时间单位。

以下截图显示这个示例执行的输出:

6

你可以看出用Task:作为前缀的任务每2秒执行一次,并且每演示500毫秒向控制台写入一次。这就是main线程睡眠的时间。当你关闭执行者,这个计划任务结束它的执行,你将不会在控制台看到更多的信息。

不止这些…

ScheduledThreadPoolExecutor 提供其他方法来调度周期性任务。这就是scheduleWithFixedRate()方法。它与scheduledAtFixedRate()方法有一 样的参数,但它们之间的差异值得注意。在scheduledAtFixedRate()方法中,第3个参数决定两个执行开始的一段时间。在 scheduledWithFixedRate()方法中,参数决定任务执行结束与下次执行开始之间的一段时间。

当你使用 shutdown()方法时,你也可以通过参数配置一个SeduledThreadPoolExecutor的行为。shutdown()方法默认的行为是,当你调用这个方法时,计划任务就结束。 你可以使用ScheduledThreadPoolExecutor类的 setContinueExistingPeriodicTasksAfterShutdownPolicy()方法设置true值改变这个行为。在调用 shutdown()方法时,周期性任务将不会结束。

参见

  • 在第4章,线程执行者中的创建一个线程执行者食谱
  • 在第4章,线程执行者中的执行者延迟运行一个任务食谱
目录
相关文章
|
3月前
|
Java 测试技术 API
【JUC】(1)带你重新认识进程与线程!!让你深层次了解线程运行的睡眠与打断!!
JUC是什么?你可以说它就是研究Java方面的并发过程。本篇是JUC专栏的第一章!带你了解并行与并发、线程与程序、线程的启动与休眠、打断和等待!全是干货!快快快!
637 2
|
存储 Java 数据库
如何处理线程池关闭时未完成的任务?
总之,处理线程池关闭时未完成的任务需要综合考虑多种因素,并根据实际情况选择合适的处理方式。通过合理的处理,可以最大程度地减少任务丢失和数据不一致等问题,确保系统的稳定运行和业务的顺利开展。
607 64
|
消息中间件 监控 Java
线程池关闭时未完成的任务如何保证数据的一致性?
保证线程池关闭时未完成任务的数据一致性需要综合运用多种方法和机制。通过备份与恢复、事务管理、任务状态记录与恢复、数据同步与协调、错误处理与补偿、监控与预警等手段的结合,以及结合具体业务场景进行分析和制定策略,能够最大程度地确保数据的一致性,保障系统的稳定运行和业务的顺利开展。同时,不断地优化和改进这些方法和机制,也是提高系统性能和可靠性的重要途径。
345 62
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
436 17
|
11月前
|
数据采集 Java 数据处理
Python实用技巧:轻松驾驭多线程与多进程,加速任务执行
在Python编程中,多线程和多进程是提升程序效率的关键工具。多线程适用于I/O密集型任务,如文件读写、网络请求;多进程则适合CPU密集型任务,如科学计算、图像处理。本文详细介绍这两种并发编程方式的基本用法及应用场景,并通过实例代码展示如何使用threading、multiprocessing模块及线程池、进程池来优化程序性能。结合实际案例,帮助读者掌握并发编程技巧,提高程序执行速度和资源利用率。
582 0
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
271 12
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
353 5
|
Java Spring
运行@Async注解的方法的线程池
自定义@Async注解线程池
536 3
|
前端开发 JavaScript 大数据
React与Web Workers:开启前端多线程时代的钥匙——深入探索计算密集型任务的优化策略与最佳实践
【8月更文挑战第31天】随着Web应用复杂性的提升,单线程JavaScript已难以胜任高计算量任务。Web Workers通过多线程编程解决了这一问题,使耗时任务独立运行而不阻塞主线程。结合React的组件化与虚拟DOM优势,可将大数据处理等任务交由Web Workers完成,确保UI流畅。最佳实践包括定义清晰接口、加强错误处理及合理评估任务特性。这一结合不仅提升了用户体验,更为前端开发带来多线程时代的全新可能。
447 1
|
3月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
195 6