天天看点

线程执行者(八)执行者周期性地运行一个任务

执行者周期性地运行一个任务

执行者框架提供threadpoolexecutor类,使用池中的线程执行并发任务,从而避免所有线程的创建操作。当你提交任务给执行者,根据它的配置,它尽快地执行任务。当它结束,任务将被执行者删除,如果你想再次运行任务,你必须再次提交任务给执行者。

但是执行者框架通过scheduledthreadpoolexecutor类可以执行周期性任务。在这个指南中,你将学习如何通过使用这个类的功能来安排一个周期性任务。

准备工作…

这个指南的例子使用eclipse ide实现。如果你使用eclipse或其他ide,如netbeans,打开它并创建一个新的java项目。

如何做…

按以下步骤来实现的这个例子:

1.创建task类,并指定它实现runnable接口。

<code>1</code>

<code>public</code> <code>class</code> <code>task </code><code>implements</code> <code>runnable {</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.实现run()方法,写入实际日期到控制台,检查任务在指定的时间内执行。

<code>@override</code>

<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>4</code>

<code>return</code> <code>"hello, world"</code><code>;</code>

<code>5</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>scheduledexecutorservice executor=executors.newscheduledthreadpool(</code><code>1</code><code>);</code>

7.写入实际日期到控制台。

<code>system.out.printf(</code><code>"main: starting at: %s\n"</code><code>,</code><code>new</code> <code>date());</code>

8.创建一个新的task对象。

<code>task task=</code><code>new</code> <code>task(</code><code>"task"</code><code>);</code>

9.使用scheduledatfixrate()方法把它提交给执行者。使用前面创建的任务,数字1,数字2和常量timeunit.seconds作为参数。这个方法返回scheduledfuture对象,它可以用来控制任务的状态。

<code>scheduledfuture&lt;?&gt; result=executor.scheduleatfixedrate(task,</code><code>1</code><code>, </code><code>2</code><code>, timeunit.seconds);</code>

10.创建10个循环步骤,写入任务下次执行的剩余时间。在循环中,使用scheduledfuture对象的getdelay()方法,获取任务下次执行的毫秒数。

<code>01</code>

<code>for</code> <code>(</code><code>int</code> <code>i=</code><code>0</code><code>; i&lt;</code><code>10</code><code>; i++){</code>

<code>02</code>

<code>system.out.printf(</code><code>"main: delay: %d\n"</code><code>,result.</code>

<code>03</code>

<code>getdelay(timeunit.milliseconds));</code>

<code>04</code>

<code>//线程睡眠500毫秒</code>

<code>05</code>

<code>try</code> <code>{</code>

<code>06</code>

<code>timeunit.milliseconds.sleep(</code><code>500</code><code>);</code>

<code>07</code>

<code>} </code><code>catch</code> <code>(interruptedexception e) {</code>

<code>08</code>

<code>e.printstacktrace();</code>

<code>09</code>

<code>10</code>

11.使用shutdown()方法关闭执行者。

<code>executor.shutdown();</code>

12.使线程睡眠5秒,检查周期性任务是否完成。

<code>timeunit.seconds.sleep(</code><code>5</code><code>);</code>

13.写入一条信息到控制台,表明程序结束。

<code>system.out.printf(</code><code>"main: finished at: %s\n"</code><code>,</code><code>new</code> <code>date());</code>

它是如何工作的…

当你想要使用执行者框架执行一个周期性任务,你需要scheduledexecutorservice对象。java建议使用 executors类创建执行者,executors类是一个执行者对象工厂。在本例中,你应该使用newscheduledthreadpool()方法,创建一个 scheduledexecutorservice对象。这个方法接收池的线程数作为参数。正如在本例中你只有一个任务,你传入了值1作为参数。

一旦你有执行者需要执行一个周期性任务,你提交任务给该执行者。你已经使用了scheduledatfixedrate()方法。此方法接收4个参数:你想要周期性执行的任务、第一次执行任务的延迟时间、两次执行之间的间隔期间、第2、3个参数的时间单位。它是timeunit类的常 量,timeunit类是个枚举类,有如下常量:days,hours,microseconds, milliseconds, minutes,,nanoseconds 和seconds。

很重要的一点需要考虑的是两次执行之间的(间隔)期间,是这两个执行开始之间的一段时间。如果你有一个花5秒执行的周期性任务,而你给一段3秒时间,同一时刻,你将会有两个任务在执行。

scheduleatfixedrate() 方法返回scheduledfuture对象,它继承future接口,这个方法和调度任务一起协同工作。scheduledfuture是一个参数化接口(校对注:scheduledfuture&lt;v&gt;)。在这个示例中,由于你的任务是非参数化的runnable对象,你必须使用 问号作为参数。

你已经使用scheduledfuture接口的一个方法。getdelay()方法返回直到任务的下次执行时间。这个方法接收一个timeunit常量,这是你想要接收结果的时间单位。

以下截图显示这个示例执行的输出:

线程执行者(八)执行者周期性地运行一个任务

你可以看出用task:作为前缀的任务每2秒执行一次,并且每演示500毫秒向控制台写入一次。这就是main线程睡眠的时间。当你关闭执行者,这个计划任务结束它的执行,你将不会在控制台看到更多的信息。

不止这些…

scheduledthreadpoolexecutor 提供其他方法来调度周期性任务。这就是schedulewithfixedrate()方法。它与scheduledatfixedrate()方法有一 样的参数,但它们之间的差异值得注意。在scheduledatfixedrate()方法中,第3个参数决定两个执行开始的一段时间。在 scheduledwithfixedrate()方法中,参数决定任务执行结束与下次执行开始之间的一段时间。

当你使用 shutdown()方法时,你也可以通过参数配置一个seduledthreadpoolexecutor的行为。shutdown()方法默认的行为是,当你调用这个方法时,计划任务就结束。 你可以使用scheduledthreadpoolexecutor类的 setcontinueexistingperiodictasksaftershutdownpolicy()方法设置true值改变这个行为。在调用 shutdown()方法时,周期性任务将不会结束。

参见

在第4章,线程执行者中的创建一个线程执行者食谱

在第4章,线程执行者中的执行者延迟运行一个任务食谱

继续阅读