執行緒執行者(八)執行者週期性地執行一個任務
宣告:本文是《 Java 7 Concurrency Cookbook 》的第四章,作者: Javier Fernández González 譯者:許巧輝 校對:方騰飛
執行者週期性地執行一個任務
執行者框架提供ThreadPoolExecutor類,使用池中的執行緒執行併發任務,從而避免所有執行緒的建立操作。當你提交任務給執行者,根據它的配置,它儘快地執行任務。當它結束,任務將被執行者刪除,如果你想再次執行任務,你必須再次提交任務給執行者。
但是執行者框架通過ScheduledThreadPoolExecutor類可以執行週期性任務。在這個指南中,你將學習如何通過使用這個類的功能來安排一個週期性任務。
準備工作…
這個指南的例子使用Eclipse IDE實現。如果你使用Eclipse或其他IDE,如NetBeans,開啟它並建立一個新的Java專案。
如何做…
按以下步驟來實現的這個例子:
1.建立Task類,並指定它實現Runnable介面。
public class Task implements Runnable {
2.宣告一個私有的、型別為String、名為name的屬性,用來儲存任務的名稱。
private String name;
3.實現Task類的構造器,初始化name屬性。
public Task(String name) { this.name=name; }
4.實現run()方法,寫入實際日期到控制檯,檢查任務在指定的時間內執行。
@Override public String call() throws Exception { System.out.printf("%s: Starting at : %s\n",name,new Date()); return "Hello, world"; }
5.實現示例的主類,建立Main類,實現main()方法。
public class Main { public static void main(String[] args) {
6.使用Executors類的newScheduledThreadPool()方法,建立ScheduledThreadPoolExecutor。傳入引數1給這個方法。
ScheduledExecutorService executor=Executors.newScheduledThreadPool(1);
7.寫入實際日期到控制檯。
System.out.printf("Main: Starting at: %s\n",new Date());
8.建立一個新的Task物件。
Task task=new Task("Task");
9.使用scheduledAtFixRate()方法把它提交給執行者。使用前面建立的任務,數字1,數字2和常量TimeUnit.SECONDS作為引數。這個方法返回ScheduledFuture物件,它可以用來控制任務的狀態。
ScheduledFuture<?> result=executor.scheduleAtFixedRate(task,1, 2, TimeUnit.SECONDS);
10.建立10個迴圈步驟,寫入任務下次執行的剩餘時間。在迴圈中,使用ScheduledFuture物件的getDelay()方法,獲取任務下次執行的毫秒數。
for (int i=0; i<10; i++){ System.out.printf("Main: Delay: %d\n",result. getDelay(TimeUnit.MILLISECONDS)); //執行緒睡眠500毫秒 try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } }
11.使用shutdown()方法關閉執行者。
executor.shutdown();
12.使執行緒睡眠5秒,檢查週期性任務是否完成。
try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
13.寫入一條資訊到控制檯,表明程式結束。
System.out.printf("Main: Finished at: %s\n",new Date());
它是如何工作的…
當你想要使用執行者框架執行一個週期性任務,你需要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章,執行緒執行者中的執行者延遲執行一個任務食譜