`
victorzhzh
  • 浏览: 202017 次
  • 来自: ...
社区版块
存档分类
最新评论

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

阅读更多

上一篇中我们看到了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很类似这里就不再举例了。

 

 

分享到:
评论
2 楼 wangdy0987 2014-04-04  
mark,仔细研究。
谢谢lz分享。
1 楼 uoton 2013-07-22  
谢谢PO主的分享.

相关推荐

Global site tag (gtag.js) - Google Analytics