執行者周期性地運作一個任務
執行者架構提供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<?> 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<</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<v>)。在這個示例中,由于你的任務是非參數化的runnable對象,你必須使用 問号作為參數。
你已經使用scheduledfuture接口的一個方法。getdelay()方法傳回直到任務的下次執行時間。這個方法接收一個timeunit常量,這是你想要接收結果的時間機關。
以下截圖顯示這個示例執行的輸出:
你可以看出用task:作為字首的任務每2秒執行一次,并且每示範500毫秒向控制台寫入一次。這就是main線程睡眠的時間。當你關閉執行者,這個計劃任務結束它的執行,你将不會在控制台看到更多的資訊。
不止這些…
scheduledthreadpoolexecutor 提供其他方法來排程周期性任務。這就是schedulewithfixedrate()方法。它與scheduledatfixedrate()方法有一 樣的參數,但它們之間的差異值得注意。在scheduledatfixedrate()方法中,第3個參數決定兩個執行開始的一段時間。在 scheduledwithfixedrate()方法中,參數決定任務執行結束與下次執行開始之間的一段時間。
當你使用 shutdown()方法時,你也可以通過參數配置一個seduledthreadpoolexecutor的行為。shutdown()方法預設的行為是,當你調用這個方法時,計劃任務就結束。 你可以使用scheduledthreadpoolexecutor類的 setcontinueexistingperiodictasksaftershutdownpolicy()方法設定true值改變這個行為。在調用 shutdown()方法時,周期性任務将不會結束。
參見
在第4章,線程執行者中的建立一個線程執行者食譜
在第4章,線程執行者中的執行者延遲運作一個任務食譜