执行者延迟运行一个任务
执行者框架提供ThreadPoolExecutor类,使用池中的线程来执行Callable和Runnable任务,这样可以避免所有线程的创建操作。当你提交一个任务给执行者,会根据执行者的配置尽快执行它。在有些使用情况下,当你对尽快执行任务不感觉兴趣。你可能想要在一段时间之后执行任务或周期性地执行任务。基于这些目的,执行者框架提供 ScheduledThreadPoolExecutor类。
在这个指南中,你将学习如何创建ScheduledThreadPoolExecutor和如何使用它安排任务在指定的时间后执行。
准备工作…
这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。
如何做…
按以下步骤来实现的这个例子:
1.创建Task类,实现Callable接口,参数化为String类型。
<code>1</code>
<code>public</code> <code>class</code> <code>Task</code><code>implements</code> <code>Callable<String> {</code>
2.声明一个私有的、类型为String、名为name的属性,用来存储任务的名称。
<code>private</code> <code>String name;</code>
3.实现Task类的构造器,初始化name属性。
<code>public</code> <code>Task(String name) {</code>
<code>2</code>
<code>this</code><code>.name=name;</code>
<code>3</code>
<code>}</code>
4.实现call()方法,写入实际日期到控制台,返回一个文本,如:Hello, world。
<code>public</code> <code>String call()</code><code>throws</code> <code>Exception {</code>
<code>System.out.printf(</code><code>"%s: Starting at : %s\n"</code><code>,name,</code><code>new</code> <code>Date());</code>
<code>return</code> <code>"Hello, world"</code><code>;</code>
<code>4</code>
5.实现示例的主类,创建Main类,实现main()方法。
<code>public</code> <code>class</code> <code>Main {</code>
<code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) {</code>
6.使用Executors类的newScheduledThreadPool()方法,创建ScheduledThreadPoolExecutor类的一个执行者。传入参数1。
<code>ScheduledThreadPoolExecutor executor=(ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(</code><code>1</code><code>);</code>
7.使用ScheduledThreadPoolExecutor实例的schedule()方法,初始化和开始一些任务(本例中5个任务)。
<code>System.out.printf(</code><code>"Main: Starting at: %s\n"</code><code>,</code><code>new</code> <code>Date());</code>
<code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i<</code><code>5</code><code>; i++) {</code>
<code>Task task=</code><code>new</code> <code>Task(</code><code>"Task "</code><code>+i);</code>
<code>executor.schedule(task,i+</code><code>1</code> <code>, TimeUnit.SECONDS);</code>
<code>5</code>
8.使用shutdown()方法关闭执行者。
<code>executor.shutdown();</code>
9.使用执行者的awaitTermination()方法,等待所有任务完成。
<code>try</code> <code>{</code>
<code>executor.awaitTermination(</code><code>1</code><code>, TimeUnit.DAYS);</code>
<code>}</code><code>catch</code> <code>(InterruptedException e) {</code>
<code>e.printStackTrace();</code>
10.写入一条信息表明程序结束时间。
<code>System.out.printf(</code><code>"Main: Ends at: %s\n"</code><code>,</code><code>new</code> <code>Date());</code>
它是如何工作的…
在这个示例中,关键的一点是Main类和ScheduledThreadPoolExecutor的管理。正如使用ThreadPoolExecutor类创建预定的执行者,Java建议利用Executors类。在本例中,你使用newScheduledThreadPool()方法。你用1作为参数传给这个方法。这个参数是你想要让线程池创建的线程数。
你必须使用schedule()方法,让执行者在一段时间后执行任务。这个方法接收3个参数,如下:
你想要执行的任务
你想要让任务在执行前等待多长时间
时间单位,指定为TimeUnit类的常数
在本例中,每个任务等待的秒数(TimeUnit.SECONDS)等于它在任务数组中的位置再加1。
注意事项:如果你想在给定时间执行一个任务,计算这个日期与当前日期的差异,使用这个差异作为任务的延迟。
以下截图显示这个示例执行的输出:

你可以看出这些任务是如何开始执行的,一秒执行一个。所有任务都是同时提交给执行者的,但每个任务比之前的任务都有1秒的延迟。
不止这些…
你也可以使用Runnable接口实现任务,因为ScheduledThreadPoolExecutor类的schedule()方法接收这两种类型(Runnable和Callable)的任务。
尽管ScheduledThreadPoolExecutor类是ThreadPoolExecutor类的子类,因此,它继承 ThreadPoolExecutor类的所有功能,但Java建议使用ScheduledThreadPoolExecutor仅适用于调度任务。
最后,你可以配置ScheduledThreadPoolExecutor的行为,当你调用shutdown()方法时,并且有待处理的任务正在等待它们延迟结束。默认的行为是,不管执行者是否结束这些任务都将被执行。你可以使用ScheduledThreadPoolExecutor类的setExecuteExistingDelayedTasksAfterShutdownPolicy()方法来改变这种行为。使用false,调用 shutdown()时,待处理的任务不会被执行。
参见
在第4章,线程执行者中的执行者执行返回结果的任务指南