上一篇中我们看到了Timer的不足之处,本篇我们将围绕这些不足之处看看ScheduledThreadPoolExecutor是如何优化的。
为了研究方便我们需要两个类:
public class Task1 implements Callable<String> {
@Override
public String call() throws Exception {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
System.out.println("Task1 running: " + new Date());
return sb.toString();
}
}
生成含有10个字符的字符串,使用Callable接口目的是我们不再任务中直接输出结果,而主动取获取任务的结果
public class LongTask implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("LongTask running: "+new Date());
TimeUnit.SECONDS.sleep(10);
return "success";
}
}
长任务类,这里我们让任务沉睡10秒然后返回一个“success”
下面我们来分析一下ScheduledThreadPoolExecutor:
1、Timer中单线程问题是否在ScheduledThreadPoolExecutor中存在?
我们先来看一下面的程序:
public class ScheduledThreadPoolExec {
public static void main(String[] args) throws InterruptedException,
ExecutionException {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
2);
ScheduledFuture future1 = executor.schedule(new Task1(), 5,
TimeUnit.SECONDS);
ScheduledFuture future2 = executor.schedule(new LongTask(), 3,
TimeUnit.SECONDS);
BlockingQueue<ScheduledFuture> blockingQueue = new ArrayBlockingQueue<ScheduledFuture>(
2, true);
blockingQueue.add(future2);
blockingQueue.add(future1);
System.out.println(new Date());
while (!blockingQueue.isEmpty()) {
ScheduledFuture future = blockingQueue.poll();
if (!future.isDone())
blockingQueue.add(future);
else
System.out.println(future.get());
}
System.out.println(new Date());
executor.shutdown();
}
}
首先,我们定义了一个ScheduledThreadPoolExecutor它的池长度是2。接着提交了两个任务:第一个任务将延迟5秒执行,第二个任务将延迟3秒执行。我们建立了一个BlockingQueue,用它来存储了ScheduledFuture,使用ScheduledFuture可以获得任务的执行结果。在一个while循环中,我们每次将一个ScheduledFuture从队列中弹出,验证它是否被执行,如果没有被执行则再次将它加入队列中,如果被执行了,这使用ScheduledFuture的get方法获取任务执行的结果。看一下执行结果:
Thu Apr 21 19:23:02 CST 2011
LongTask running: Thu Apr 21 19:23:05 CST 2011
Task1 running: Thu Apr 21 19:23:07 CST 2011
h1o2wd942e
success
Thu Apr 21 19:23:15 CST 2011
我们看到长任务先运行,因为长任务只等待了3秒,然后是输出字符串的任务运行,两个任务开始时间相差2秒,而先输出了字符串而后才是长任务运行的结果success,最后我们查看一下整体的开始和结束时间相差了13秒。
这说明在ScheduledThreadPoolExecutor中它不是以一个线程运行任务的,而是以多个线程,如果用一个线程运行任务,那么长任务运行完之前是不会运行输出字符串任务的。其实这个“多个任务“是我们自己指定的注意一下标红的代码,如果我们把这行代码改为:
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
那么运行结果就会发生变化,
Thu Apr 21 19:36:56 CST 2011
LongTask running: Thu Apr 21 19:36:59 CST 2011
success
Task1 running: Thu Apr 21 19:37:09 CST 2011
y981iqd0or
Thu Apr 21 19:37:09 CST 2011
这时其实和使用Timer运行结果是一样的。任务是在一个线程里顺序执行的。
2、Timer中一但有运行时异常报出后续任务是否还会正常运行?
为了研究这个问题,我们还是需要一个能够抛出异常的任务,如下:
public class TimerExceptionTask extends TimerTask {
@Override
public void run() {
System.out.println("TimerExceptionTask: "+new Date());
throw new RuntimeException();
}
}
我们对上面运行任务的代码做一点点小小的修改,先运行两个抛出异常的任务,如下:
public class ScheduledThreadPoolExec {
public static void main(String[] args) throws InterruptedException,
ExecutionException {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
2);
ScheduledFuture future1 = executor.schedule(new Task1(), 5,
TimeUnit.SECONDS);
ScheduledFuture future2 = executor.schedule(new LongTask(), 3,
TimeUnit.SECONDS);
executor.schedule(new TimerExceptionTask(), 1, TimeUnit.SECONDS);
executor.schedule(new TimerExceptionTask(), 2, TimeUnit.SECONDS);
BlockingQueue<ScheduledFuture> blockingQueue = new ArrayBlockingQueue<ScheduledFuture>(
2, true);
blockingQueue.add(future2);
blockingQueue.add(future1);
System.out.println(new Date());
while (!blockingQueue.isEmpty()) {
ScheduledFuture future = blockingQueue.poll();
if (!future.isDone())
blockingQueue.add(future);
else
System.out.println(future.get());
}
System.out.println(new Date());
executor.shutdown();
}
}
注意,标红的代码,如果这两个代码抛出错误后会影响后续任务,那么就应该在此终止,但是看一下结果,
Thu Apr 21 19:40:15 CST 2011
TimerExceptionTask: Thu Apr 21 19:40:16 CST 2011
TimerExceptionTask: Thu Apr 21 19:40:17 CST 2011
LongTask running: Thu Apr 21 19:40:18 CST 2011
Task1 running: Thu Apr 21 19:40:20 CST 2011
v5gcf01iiz
success
Thu Apr 21 19:40:28 CST 2011
后续任务仍然执行,可能会有朋友说:“你上面的池设置的是2,所以很有可能是那两个抛出异常的任务都在同一个线程中执行,而另一个线程执行了后续的任务”。那我们就把ScheduledThreadPoolExecutor设置成1看看,结果如下:
Thu Apr 21 19:43:00 CST 2011
TimerExceptionTask: Thu Apr 21 19:43:01 CST 2011
TimerExceptionTask: Thu Apr 21 19:43:02 CST 2011
LongTask running: Thu Apr 21 19:43:03 CST 2011
success
Task1 running: Thu Apr 21 19:43:13 CST 2011
33kgv8onnd
Thu Apr 21 19:43:13 CST 2011
后续任务也执行了,所以说ScheduledThreadPoolExecutor不会像Timer那样有线程泄漏现象。
对于周期性执行和Timer很类似这里就不再举例了。
分享到:
相关推荐
定时任务·
在分布式系统中,定时任务库扮演着至关重要的角色,它可以帮助我们执行诸如数据同步、清理过期记录、发送通知等周期性任务。`distributed-cron` 的设计目标是简化这些任务的实现,并确保在分布式环境下的正确性和...
Java Web 使用监听器实现定时周期性执行任务是一种常见的需求,特别是在服务器端需要定期进行某些维护操作,例如数据备份、清理过期数据或者发送通知。在这个"java web使用监听器实现定时周期性执行任务demo"中,...
这在许多场景下非常有用,例如数据备份、日志清理、发送邮件等周期性任务。ThinkPHP提供了`think-cron`组件来简化这个过程。 二、Crontab简介 Crontab是Linux系统中用于调度周期性任务的命令,它可以按照设定的...
界面采用bootstrap和JEasyUI技术实现,提供三种任务运行规则:一次性、周期性、自定义 1、一次性(i:立即运行;ii:在规定的时间刻运行) 2、周期性(i:按小时;ii:按天 iii:按周; iv:按月(日);v: 按月(星期) ...
3. **灵活的任务策略**:支持简单定时(如cron表达式)、依赖任务、周期任务等多种调度策略。 4. **监控与告警**:提供任务运行状态的实时监控,异常时可发送告警通知。 5. **扩展性**:niubi-job采用模块化设计,...
这些任务可以是简单的一次性操作,也可以是周期性的重复任务。定时任务的重要性在于其能够帮助系统自动化执行日常维护工作,提高效率,减少人工干预的需求。 二、常见定时任务工具 1. **Cron (Unix/Linux)**:Unix...
6. **定时器**:提供周期性和一次性定时功能,为系统定时任务提供支持。 7. **中断服务**:高效处理硬件中断,确保实时性。 在V2.90中,uCOS-II进一步优化了这些功能,提高了系统的稳定性和效率。例如,任务调度...
在IT行业中,定时任务是任何系统中不可或缺的一部分,尤其在微服务架构中,它们用于执行周期性任务,如数据同步、日志清理、备份等。在本篇文章中,我们将深入探讨“kratos定时任务-kratos-cron”这一主题,了解如何...
尽管WorkManager并非专门为定时任务设计,但其灵活的工作调度机制可以方便地实现周期性任务。 在实际应用中,选择哪种方式取决于任务的具体需求。如果只是简单的UI更新,Handler和Looper可能是最佳选择,因为它们...
- 除了Quartz,还有其他的定时任务库,如Apache Commons Executor的ScheduledThreadPoolExecutor,它提供了更灵活的线程池实现定时任务。 - Spring Boot中也可以使用`@Async`注解配合`@EnableAsync`实现异步任务,...
uCOS-II提供了定时器服务,允许设置周期性和一次性定时事件,这对于周期性任务的执行和事件触发非常重要。 7. **可移植性** uCOS-II设计时考虑了广泛的硬件平台兼容性,因此能在多种微处理器上运行,只需要适配...
Java开发案例-springboot-29-整合ShedLock实现分布式定时任务(redis版)-源代码+文档.rar Java开发案例-springboot-29-整合ShedLock实现分布式定时任务(redis版)-源代码+文档.rar Java开发案例-springboot-29-整合...
Celery是一个基于Python开发的分布式任务队列系统,它支持后台任务的异步处理、周期性任务的调度和定时任务的执行。它使得任务的执行不受限于用户界面或用户交互,从而提高应用程序的响应性和效率。 Celery的核心...
ExecutorService是线程池的基本服务接口,ThreadPoolExecutor是其具体实现,用于管理一组可重用的工作线程,而ScheduledThreadPoolExecutor则支持定时及周期性执行任务。 任务(Task)是在线程池中执行的工作单元,...
Java开发案例-springboot-11-定时任务入门-源代码+文档.rar Java开发案例-springboot-11-定时任务入门-源代码+文档.rar Java开发案例-springboot-11-定时任务入门-源代码+文档.rarJava开发案例-springboot-11-定时...
Quartz是另一个流行的任务调度库,它允许开发者创建和管理定时任务。当我们需要在Spring应用中实现动态定时任务时,就需要将这两者结合起来。这个"spring-quartz-demo"项目就是一个很好的示例,它展示了如何在Spring...
一款支持自定义定时任务的chatgpt-on-wechat插件-timetask
Cron是Linux系统中用于调度周期性任务的守护进程(Daemon),允许用户在固定时间或周期性地执行命令或脚本。通过Cron,用户可以设定复杂的定时任务,如定期备份数据、清理日志、发送邮件提醒等。 #### 二、Cron的...
除此之外,uCOS-II还包括定时器服务,可以设置周期性或一次性定时任务,这对于实时系统的精确时间控制非常重要。同时,它还支持任务间的延迟和挂起功能,使得任务可以在等待特定条件满足后再继续执行。 在移植性...