定时且周期性的任务研究II--ScheduledThreadPoolExecutor
http://victorzhzh.iteye.com/blog/1011635
上一篇中我们看到了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 {
- <strong><span style="color: #ff0000;">ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
- 2);</span>
- </strong>
- 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);
- <strong><span style="color: #ff0000;">executor.schedule(new TimerExceptionTask(), 1, TimeUnit.SECONDS);
- executor.schedule(new TimerExceptionTask(), 2, TimeUnit.SECONDS);</span>
- </strong>
- 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很类似这里就不再举例了。
定时且周期性的任务研究II--ScheduledThreadPoolExecutor的更多相关文章
- 定时且周期性的任务研究I--Timer
很多时候我们希望任务可以定时的周期性的执行,在最初的JAVA工具类库中,通过Timer可以实现定时的周期性的需求,但是有一定的缺陷,例如:Timer是基于绝对时间的而非支持相对时间,因此Timer对系 ...
- (CSDN 迁移) JAVA多线程实现-支持定时与周期性任务的线程池(newScheduledThreadPool)
前几篇文章中分别介绍了 单线程化线程池(newSingleThreadExecutor) 可控最大并发数线程池(newFixedThreadPool) 可回收缓存线程池(newCachedThread ...
- JavaScript定时机制setTimeout与setInterval研究
JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如 setTimeout( ...
- Executor(二)ThreadPoolExecutor、ScheduledThreadPoolExecutor 及 Executors 工厂类
Executor(二)ThreadPoolExecutor.ScheduledThreadPoolExecutor 及 Executors 工厂类 Java 中的线程池类有两个,分别是:ThreadP ...
- java笔记--使用线程池优化多线程编程
使用线程池优化多线程编程 认识线程池 在Java中,所有的对象都是需要通过new操作符来创建的,如果创建大量短生命周期的对象,将会使得整个程序的性能非常的低下.这种时候就需要用到了池的技术,比如数据库 ...
- java线程池,阿里为什么不允许使用Executors?
带着问题 阿里Java代码规范为什么不允许使用Executors快速创建线程池? 下面的代码输出是什么? ThreadPoolExecutor executor = new ThreadPoolExe ...
- 阿里P7告诉你什么是java并发包、线程池、锁
并发包 java.util.concurrent从jdk1.5开始新加入的一个包,致力于解决并发编程的线程安全问题,使用户能够更为快捷方便的编写多线程情况下的并发程序. 同步容器 同步容器只有包括Ve ...
- ExecutorService 线程池详解
1.什么是ExecutorService,为什么要使用线程池? 许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务,每当一个请求到达就创建一个新线程,然后在新线程中为请求服务,但是频繁创建新 ...
- ScheduledThreadPoolExecutor中定时周期任务的实现源码分析
ScheduledThreadPoolExecutor是一个定时任务线程池,相比于ThreadPoolExecutor最大的不同在于其阻塞队列的实现 首先看一下其构造方法: public Schedu ...
随机推荐
- WCF:调用方未由服务器进行身份验证
错误描述: 1. WCF:调用方未由服务器进行身份验证 2. 无法处理消息.这很可能是因为操作“http://tempuri.org/ISCCLSvc/GetCarriersByWareHouse”不 ...
- crossdomain 可用
<cross-domain-policy> <allow-access-from domain="*"/> <allow-http-request-h ...
- [转]php 在各种web服务器的运行模式
一.php在apache中运行模式 php在apache中一共有三种工作方式:CGI模式.FastCGI模式.Apache 模块DLL) 以下分别比较: 1. CGI模式与模块模式比较: php在ap ...
- Javascript教程
Javascript教程 laiqun@msn.cn Contents 1. javascript嵌入方式 2. javascript语法 3. 数据类型 4. javascript变量 5. 字符串 ...
- AJAX校验商品价格(类似校验用户名)
服务器端程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <%@ WebHandler Language=" ...
- namenode ha
http://blog.csdn.net/totxian/article/details/45248399 http://www.aboutyun.com/thread-13679-1-1.html ...
- 如何修改android工程的包名?
在我们android项目开发到一定的程度时由于需要,我们必须修改一下工程的包名,以便更好的发布我们的项目.但是在这个过程中有时候修改好了之后会出现一些错误.下面由小编一步步教你如何更改包名,和解决出现 ...
- 报错 BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext 的解决方法
这个普遍是因为tomcat 的 jar包问题,重新导入一下tomcat的jar包就OK了.
- Android OpenGL ES(二)OpenGL ES管道(Pipeline) .
大部分图形系统都可以比作工厂中的装配线(Assemble line)或者称为管道(Pipeline).前一道的输出作为下道工序的输入.主CPU发出一个绘图指令,然后可能由硬件部件完成坐标变换,裁剪,添 ...
- Swift & OC 混编 浅析
转载自:http://www.infoq.com/cn/articles/wangyi-cartoon-swift-mixed-practice?utm_campaign=rightbar_v2&am ...