定时且周期性的任务研究II--ScheduledThreadPoolExecutor

http://victorzhzh.iteye.com/blog/1011635

上一篇中我们看到了Timer的不足之处,本篇我们将围绕这些不足之处看看ScheduledThreadPoolExecutor是如何优化的。

为了研究方便我们需要两个类:

Java代码  

  1. public class Task1 implements Callable<String> {
  2. @Override
  3. public String call() throws Exception {
  4. String base = "abcdefghijklmnopqrstuvwxyz0123456789";
  5. Random random = new Random();
  6. StringBuffer sb = new StringBuffer();
  7. for (int i = 0; i < 10; i++) {
  8. int number = random.nextInt(base.length());
  9. sb.append(base.charAt(number));
  10. }
  11. System.out.println("Task1 running: " + new Date());
  12. return sb.toString();
  13. }
  14. }

生成含有10个字符的字符串,使用Callable接口目的是我们不再任务中直接输出结果,而主动取获取任务的结果

Java代码  

  1. public class LongTask implements Callable<String> {
  2. @Override
  3. public String call() throws Exception {
  4. System.out.println("LongTask running: "+new Date());
  5. TimeUnit.SECONDS.sleep(10);
  6. return "success";
  7. }
  8. }

长任务类,这里我们让任务沉睡10秒然后返回一个“success”

下面我们来分析一下ScheduledThreadPoolExecutor:

1、Timer中单线程问题是否在ScheduledThreadPoolExecutor中存在?

我们先来看一下面的程序:

Java代码  

  1. public class ScheduledThreadPoolExec {
  2. public static void main(String[] args) throws InterruptedException,
  3. ExecutionException {
  4. <strong><span style="color: #ff0000;">ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
  5. 2);</span>
  6. </strong>
  7. ScheduledFuture future1 = executor.schedule(new Task1(), 5,
  8. TimeUnit.SECONDS);
  9. ScheduledFuture future2 = executor.schedule(new LongTask(), 3,
  10. TimeUnit.SECONDS);
  11. BlockingQueue<ScheduledFuture> blockingQueue = new ArrayBlockingQueue<ScheduledFuture>(
  12. 2, true);
  13. blockingQueue.add(future2);
  14. blockingQueue.add(future1);
  15. System.out.println(new Date());
  16. while (!blockingQueue.isEmpty()) {
  17. ScheduledFuture future = blockingQueue.poll();
  18. if (!future.isDone())
  19. blockingQueue.add(future);
  20. else
  21. System.out.println(future.get());
  22. }
  23. System.out.println(new Date());
  24. executor.shutdown();
  25. }
  26. }

首先,我们定义了一个ScheduledThreadPoolExecutor它的池长度是2。接着提交了两个任务:第一个任务将延迟5秒执行,第二个任务将延迟3秒执行。我们建立了一个BlockingQueue,用它来存储了ScheduledFuture,使用ScheduledFuture可以获得任务的执行结果。在一个while循环中,我们每次将一个ScheduledFuture从队列中弹出,验证它是否被执行,如果没有被执行则再次将它加入队列中,如果被执行了,这使用ScheduledFuture的get方法获取任务执行的结果。看一下执行结果:

Java代码  

  1. Thu Apr 21 19:23:02 CST 2011
  2. LongTask running: Thu Apr 21 19:23:05 CST 2011
  3. Task1 running: Thu Apr 21 19:23:07 CST 2011
  4. h1o2wd942e
  5. success
  6. Thu Apr 21 19:23:15 CST 2011

我们看到长任务先运行,因为长任务只等待了3秒,然后是输出字符串的任务运行,两个任务开始时间相差2秒,而先输出了字符串而后才是长任务运行的结果success,最后我们查看一下整体的开始和结束时间相差了13秒。

这说明在ScheduledThreadPoolExecutor中它不是以一个线程运行任务的,而是以多个线程,如果用一个线程运行任务,那么长任务运行完之前是不会运行输出字符串任务的。其实这个“多个任务“是我们自己指定的注意一下标红的代码,如果我们把这行代码改为:

Java代码  

  1. ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);

那么运行结果就会发生变化,

Java代码  

  1. Thu Apr 21 19:36:56 CST 2011
  2. LongTask running: Thu Apr 21 19:36:59 CST 2011
  3. success
  4. Task1 running: Thu Apr 21 19:37:09 CST 2011
  5. y981iqd0or
  6. Thu Apr 21 19:37:09 CST 2011

这时其实和使用Timer运行结果是一样的。任务是在一个线程里顺序执行的。

2、Timer中一但有运行时异常报出后续任务是否还会正常运行?

为了研究这个问题,我们还是需要一个能够抛出异常的任务,如下:

Java代码  

  1. public class TimerExceptionTask extends TimerTask {
  2. @Override
  3. public void run() {
  4. System.out.println("TimerExceptionTask: "+new Date());
  5. throw new RuntimeException();
  6. }
  7. }

我们对上面运行任务的代码做一点点小小的修改,先运行两个抛出异常的任务,如下:

Java代码  

  1. public class ScheduledThreadPoolExec {
  2. public static void main(String[] args) throws InterruptedException,
  3. ExecutionException {
  4. ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
  5. 2);
  6. ScheduledFuture future1 = executor.schedule(new Task1(), 5,
  7. TimeUnit.SECONDS);
  8. ScheduledFuture future2 = executor.schedule(new LongTask(), 3,
  9. TimeUnit.SECONDS);
  10. <strong><span style="color: #ff0000;">executor.schedule(new TimerExceptionTask(), 1, TimeUnit.SECONDS);
  11. executor.schedule(new TimerExceptionTask(), 2, TimeUnit.SECONDS);</span>
  12. </strong>
  13. BlockingQueue<ScheduledFuture> blockingQueue = new ArrayBlockingQueue<ScheduledFuture>(
  14. 2, true);
  15. blockingQueue.add(future2);
  16. blockingQueue.add(future1);
  17. System.out.println(new Date());
  18. while (!blockingQueue.isEmpty()) {
  19. ScheduledFuture future = blockingQueue.poll();
  20. if (!future.isDone())
  21. blockingQueue.add(future);
  22. else
  23. System.out.println(future.get());
  24. }
  25. System.out.println(new Date());
  26. executor.shutdown();
  27. }
  28. }

注意,标红的代码,如果这两个代码抛出错误后会影响后续任务,那么就应该在此终止,但是看一下结果,

Java代码  

  1. Thu Apr 21 19:40:15 CST 2011
  2. TimerExceptionTask: Thu Apr 21 19:40:16 CST 2011
  3. TimerExceptionTask: Thu Apr 21 19:40:17 CST 2011
  4. LongTask running: Thu Apr 21 19:40:18 CST 2011
  5. Task1 running: Thu Apr 21 19:40:20 CST 2011
  6. v5gcf01iiz
  7. success
  8. Thu Apr 21 19:40:28 CST 2011

后续任务仍然执行,可能会有朋友说:“你上面的池设置的是2,所以很有可能是那两个抛出异常的任务都在同一个线程中执行,而另一个线程执行了后续的任务”。那我们就把ScheduledThreadPoolExecutor设置成1看看,结果如下:

Java代码  

  1. Thu Apr 21 19:43:00 CST 2011
  2. TimerExceptionTask: Thu Apr 21 19:43:01 CST 2011
  3. TimerExceptionTask: Thu Apr 21 19:43:02 CST 2011
  4. LongTask running: Thu Apr 21 19:43:03 CST 2011
  5. success
  6. Task1 running: Thu Apr 21 19:43:13 CST 2011
  7. 33kgv8onnd
  8. Thu Apr 21 19:43:13 CST 2011

后续任务也执行了,所以说ScheduledThreadPoolExecutor不会像Timer那样有线程泄漏现象。

对于周期性执行和Timer很类似这里就不再举例了。

时间: 2024-08-14 11:20:01

定时且周期性的任务研究II--ScheduledThreadPoolExecutor的相关文章

定时且周期性的任务研究I--Timer

很多时候我们希望任务可以定时的周期性的执行,在最初的JAVA工具类库中,通过Timer可以实现定时的周期性的需求,但是有一定的缺陷,例如:Timer是基于绝对时间的而非支持相对时间,因此Timer对系统时钟比较敏感.虽然有一定的问题,但是我们还是从这个最简单的实现开始研究. 首先,我们准备一些讨论问题的类:TimerTask1和TimerLongTask,如下 Java代码   public class TimerTask1 extends TimerTask { @Override publi

JavaScript定时机制setTimeout与setInterval研究

JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如 setTimeout(function() { alert('你好!'); }, 0); setInterval(callbackFunction, 100); 认为setTimeout中的问候方法会立即被执行,因为这并不是凭空而说,而是JavaScript API文档明确定义第二个参数意义为隔多少毫秒后,回调方法就会被

java笔记--使用线程池优化多线程编程

使用线程池优化多线程编程 认识线程池 在Java中,所有的对象都是需要通过new操作符来创建的,如果创建大量短生命周期的对象,将会使得整个程序的性能非常的低下.这种时候就需要用到了池的技术,比如数据库连接池,线程池等. 在java1.5之后,java自带了线程池,在util包下新增了concurrent包,这个包主要作用就是介绍java线程和线程池如何使用的. 在包java.util.concurrent下的 Executors类中定义了Executor.ExecutorService.Sche

周期性线程池与主要源码解析

之前学习ThreadPool的使用以及源码剖析,并且从面试的角度去介绍知识点的解答.今天小强带来周期性线程池的使用和重点源码剖析. ScheduledThreadPoolExecutor ScheduledThreadPoolExecutor:用来处理延时任务或定时任务 定时线程池类的类结构图 ScheduledThreadPoolExecutor接收ScheduleFutureTask类型的任务,是线程池调度任务的最小单位. 它采用DelayQueue存储等待的任务: 1.DelayQueue

Spring整合定时任务调度框架Quartz实战

定时的任务处理在程序开发中应用的相当普遍,之前一直使用JDK的Timer类库来做任务调度功能不是很方便,因为它不能像cron服务那样可以指定具体年.月.日.时和分的时间,我们只能将时间通过换算成微秒后传给它,而在quartz中我们只需要设置cronExpression就可以完成定时的周期性的方法调用.Quartz中最核心的是任务调度器Scheduler,它负责管理Job,Trigger和 Calendar,而每一个Job就是一个需要执行任务的java类,在Schelduler调度任务时 执行的就

ScheduledThreadPoolExecutor实现原理

ScheduledThreadPoolExecutor实现原理 博客分类: concurrency java 自jdk1.5开始,Java开始提供ScheduledThreadPoolExecutor类来支持周期性任务的调度,在这之前,这些工作需要依靠Timer/TimerTask或者其它第三方工具来完成.但Timer有着不少缺陷,如Timer是单线程模式,调度多个周期性任务时,如果某个任务耗时较久就会影响其它任务的调度:如果某个任务出现异常而没有被catch则可能导致唯一的线程死掉而所有任务都

22.线程池之ScheduledThreadPoolExecutor

1. ScheduledThreadPoolExecutor简介 ScheduledThreadPoolExecutor可以用来在给定延时后执行异步任务或者周期性执行任务,相对于任务调度的Timer来说,其功能更加强大,Timer只能使用一个后台线程执行任务,而ScheduledThreadPoolExecutor则可以通过构造函数来指定后台线程的个数.ScheduledThreadPoolExecutor类的UML图如下: 从UML图可以看出,ScheduledThreadPoolExecut

2015Java面试指南(一)

? 前言 2015年可以说是Java程序员的"大年",随着2014年11月乌镇互联网的闭幕,互联网行业是真的迎来了春天.2015年大量的创业型公司的兴起,对Java中高级职位的需求几乎可以用抢人的程度,跳槽人员的薪资普通在30%-50%的涨幅(一方面是因为市场需求.另一方面企业也在努力打破好多年员工待遇倒挂现象,即新入职员工的工资总比老员工高),更高的也不罕见,这是近几年来不曾出现过的. 同时2015年也是Java20岁的生日,Java一路走来,打破了多次"将死"的

java并发编程-Executor框架

一 简介 线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的.在jdk1.5之后这一情况有了很大的改观.Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用.为我们在开发中处理线程的问题提供了非常大的帮助. 二:线程池 线程池的作用: 线程池作用就是限制系统中执行线程的数量.     根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资源,多了造成