1. 程式人生 > >執行緒執行者(八)執行者週期性地執行一個任務

執行緒執行者(八)執行者週期性地執行一個任務

宣告:本文是《 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常量,這是你想要接收結果的時間單位。

以下截圖顯示這個示例執行的輸出:

6

你可以看出用Task:作為字首的任務每2秒執行一次,並且每演示500毫秒向控制檯寫入一次。這就是main執行緒睡眠的時間。當你關閉執行者,這個計劃任務結束它的執行,你將不會在控制檯看到更多的資訊。

不止這些…

ScheduledThreadPoolExecutor 提供其他方法來排程週期性任務。這就是scheduleWithFixedRate()方法。它與scheduledAtFixedRate()方法有一 樣的引數,但它們之間的差異值得注意。在scheduledAtFixedRate()方法中,第3個引數決定兩個執行開始的一段時間。在 scheduledWithFixedRate()方法中,引數決定任務執行結束與下次執行開始之間的一段時間。

當你使用 shutdown()方法時,你也可以通過引數配置一個SeduledThreadPoolExecutor的行為。shutdown()方法預設的行為是,當你呼叫這個方法時,計劃任務就結束。 你可以使用ScheduledThreadPoolExecutor類的 setContinueExistingPeriodicTasksAfterShutdownPolicy()方法設定true值改變這個行為。在呼叫 shutdown()方法時,週期性任務將不會結束。

參見

  • 在第4章,執行緒執行者中的建立一個執行緒執行者食譜
  • 在第4章,執行緒執行者中的執行者延遲執行一個任務食譜