Java提供的Time類可以周期性地或者延期執行任務,但是有時我們需要并行執行同樣的任務,這個時候如果建立多個Time對象會給系統帶來負擔,解決辦法是将定時任務放到線程池中執行。
Java的ScheduledThreadPoolExecutor類實作了ScheduledExecutorService接口中定義的以不同方法執行任務的方法。
之前,我寫過一篇關于Java ThreadPoolExecutor的文章中使用了Executors建立線程池。Executors類也提供了工廠方法建立ScheduledThreadPoolExecutor,并且可以設定線程池中的線程。
假設有下面簡單的Runnable類
WorkerThread.java:
<col>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<code>package</code><code>com.journaldev.threads;</code>
<code>import</code><code>java.util.Date;</code>
<code>public</code><code>class</code><code>WorkerThread </code><code>implements</code><code>Runnable{</code>
<code>private</code><code>String command;</code>
<code>public</code><code>WorkerThread(String s){</code>
<code>this</code><code>.command=s;</code>
<code>}</code>
<code>@Override</code>
<code>public</code><code>void</code><code>run() {</code>
<code>System.out.println(Thread.currentThread().getName()+</code><code>" Start. Time = "</code><code>+</code><code>new</code><code>Date());</code>
<code>processCommand();</code>
<code>System.out.println(Thread.currentThread().getName()+</code><code>" End. Time = "</code><code>+</code><code>new</code><code>Date());</code>
<code>private</code><code>void</code><code>processCommand() {</code>
<code>try</code><code>{</code>
<code>Thread.sleep(</code><code>5000</code><code>);</code>
<code>}</code><code>catch</code><code>(InterruptedException e) {</code>
<code>e.printStackTrace();</code>
<code>public</code><code>String toString(){</code>
<code>return</code><code>this</code><code>.command;</code>
下面的例子中worker線程将被延期10s執行上面的Rnnable類大約花費5s執行任務
ScheduledThreadPool.java:
<code>import</code><code>java.util.concurrent.Executors;</code>
<code>import</code><code>java.util.concurrent.ScheduledExecutorService;</code>
<code>import</code><code>java.util.concurrent.TimeUnit;</code>
<code>public</code><code>class</code><code>ScheduledThreadPool {</code>
<code>public</code><code>static</code><code>void</code> <code>main(String[] args) </code><code>throws</code><code>InterruptedException {</code>
<code>ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(</code><code>5</code><code>);</code>
<code>//schedule to run after sometime</code>
<code>System.out.println(</code><code>"Current Time = "</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>3</code><code>; i++){</code>
<code>Thread.sleep(</code><code>1000</code><code>);</code>
<code>WorkerThread worker = </code><code>new</code><code>WorkerThread(</code><code>"do heavy processing"</code><code>);</code>
<code>scheduledThreadPool.schedule(worker,</code><code>10</code><code>, TimeUnit.SECONDS);</code>
<code>//add some delay to let some threads spawn by scheduler</code>
<code>Thread.sleep(</code><code>30000</code><code>);</code>
<code>scheduledThreadPool.shutdown();</code>
<code>while</code><code>(!scheduledThreadPool.isTerminated()){</code>
<code>//wait for all tasks to finish</code>
<code>System.out.println(</code><code>"Finished all threads"</code><code>);</code>
運作上面的程式,可以得到下面的輸出,由此可以确認任務在10s後才執行。
<code>Current Time = Tue Oct 29 15:10:03 IST 2013</code>
<code>pool-1-thread-1 Start. Time = Tue Oct 29 15:10:14 IST 2013</code>
<code>pool-1-thread-2 Start. Time = Tue Oct 29 15:10:15 IST 2013</code>
<code>pool-1-thread-3 Start. Time = Tue Oct 29 15:10:16 IST 2013</code>
<code>pool-1-thread-1 End. Time = Tue Oct 29 15:10:19 IST 2013</code>
<code>pool-1-thread-2 End. Time = Tue Oct 29 15:10:20 IST 2013</code>
<code>pool-1-thread-3 End. Time = Tue Oct 29 15:10:21 IST 2013</code>
<code>Finished all threads</code>
注意到所有的schedule方法都傳回了ScheduledFuture執行個體,可以用于擷取線程狀态資訊和延遲時間。ScheduledFuture接口繼承Future接口,更多資訊見Java Callable Future Example.
在ScheduledExecutorService中至少有2個方法可用于周期性執行任務。
scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)
我們可以使用該方法延遲執行任務,設定任務的執行周期。時間周期從線程池中首先開始執行的線程算起,是以假設period為1s,線程執行了5s,那麼下一個線程在第一個線程運作完後會很快被執行。
比如下面的代碼
<code>for</code><code>(</code><code>int</code><code>i = </code><code>0</code><code>; i < </code><code>3</code><code>; i++) {</code>
<code>// schedule task to execute at fixed rate</code>
<code>scheduledThreadPool.scheduleAtFixedRate(worker,</code><code>0</code><code>,</code><code>10</code><code>,</code>
<code>TimeUnit.SECONDS);</code>
輸出
<code>Current Time = Tue Oct 29 16:10:00 IST 2013</code>
<code>pool-1-thread-1 Start. Time = Tue Oct 29 16:10:01 IST 2013</code>
<code>pool-1-thread-2 Start. Time = Tue Oct 29 16:10:02 IST 2013</code>
<code>pool-1-thread-3 Start. Time = Tue Oct 29 16:10:03 IST 2013</code>
<code>pool-1-thread-1 End. Time = Tue Oct 29 16:10:06 IST 2013</code>
<code>pool-1-thread-2 End. Time = Tue Oct 29 16:10:07 IST 2013</code>
<code>pool-1-thread-3 End. Time = Tue Oct 29 16:10:08 IST 2013</code>
<code>pool-1-thread-1 Start. Time = Tue Oct 29 16:10:11 IST 2013</code>
<code>pool-1-thread-4 Start. Time = Tue Oct 29 16:10:12 IST 2013</code>
scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)
該方法可被用于延遲周期性執行任務,delaytime是線程停止執行到下一次開始執行之間的延遲時間,假設有下面的代碼
<code>scheduledThreadPool.scheduleWithFixedDelay(worker,</code><code>0</code><code>,</code><code>1</code><code>,</code>
輸出結果
<code>Current Time = Tue Oct 29 16:14:13 IST 2013</code>
<code>pool-1-thread-1 Start. Time = Tue Oct 29 16:14:14 IST 2013</code>
<code>pool-1-thread-2 Start. Time = Tue Oct 29 16:14:15 IST 2013</code>
<code>pool-1-thread-3 Start. Time = Tue Oct 29 16:14:16 IST 2013</code>
<code>pool-1-thread-1 End. Time = Tue Oct 29 16:14:19 IST 2013</code>
<code>pool-1-thread-2 End. Time = Tue Oct 29 16:14:20 IST 2013</code>
<code>pool-1-thread-1 Start. Time = Tue Oct 29 16:14:20 IST 2013</code>
<code>pool-1-thread-3 End. Time = Tue Oct 29 16:14:21 IST 2013</code>
<code>pool-1-thread-4 Start. Time = Tue Oct 29 16:14:21 IST 2013</code>