1. 程式人生 > >定製併發類(三)實現一個基於優先順序的Executor類

定製併發類(三)實現一個基於優先順序的Executor類

宣告:本文是《 Java 7 Concurrency Cookbook 》的第七章,作者: Javier Fernández González     譯者:許巧輝

實現一個基於優先順序的Executor類

在Java併發API的第一個版本中,你必須建立和執行應用程式中的所有執行緒。在Java版本5中,隨著執行者框架(Executor framework)的出現,對於併發任務的執行,一個新的機制被引進。

使用執行者框架(Executor framework),你只要實現你的任務並把它們提交給執行者。這個執行者負責執行你的任務的執行緒的建立和執行。

在內部,一個執行者使用一個阻塞佇列來儲存待處理任務。以任務到達執行者的順序來儲存。一個可能的替代就是使用一個優先順序列隊來儲存新的任務。這樣,如果一個高優先順序的新任務到達執行者,它將比其他已經在等待一個執行緒來執行它們,且低優先順序的任務先執行。

在這個指南中,你將學習如何實現一個執行者,它將使用優先順序佇列來儲存你提交執行的任務。

準備工作

這個指南的例子使用Eclipse IDE實現。如果你使用Eclipse或其他IDE,如NetBeans,開啟它並建立一個新的Java專案。

如何做…

按以下步驟來實現的這個例子:

1.建立一個MyPriorityTask類,它實現Runnable介面和引數化為MyPriorityTask類的Comparable介面。

public class MyPriorityTask implements Runnable,
Comparable<MyPriorityTask> {

2.宣告一個私有的、int型別的屬性priority。

private int priority;

3.宣告一個私有的、String型別的屬性name。

private String name;

4.實現這個類的構造器,並初始化它的屬性。

public MyPriorityTask(String name, int priority) {
this.name=name;
this.priority=priority;
}

5.實現一個方法來返回priority屬性的值。

public int getPriority(){
return priority;
}

6.實現宣告在Comparable介面中的compareTo()方法。它接收一個MyPriorityTask物件作為引數,比較這兩個物件(當前物件和引數物件)的優先順序。讓優先順序高的任務先於優先順序低的任務執行。

@Override
public int compareTo(MyPriorityTask o) {
if (this.getPriority() < o.getPriority()) {
return 1;
}
if (this.getPriority() > o.getPriority()) {
return -1;
}
return 0;
}

7.實現run()方法。令當前執行緒睡眠2秒。

@Override
public void run() {
System.out.printf("MyPriorityTask: %s Priority :
%d\n",name,priority);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

8.實現這個例子的主類,通過建立Main類,並實現main()方法。

public class Main {
public static void main(String[] args) {

9.建立一個ThreadPoolExecutor物件,名為executor。使用引數化為Runnable介面的PriorityBlockingQueue作為執行者用來儲存待處理任務的佇列。

ThreadPoolExecutor executor=new ThreadPoolExecutor(2,2,1,TimeU
nit.SECONDS,new PriorityBlockingQueue<Runnable>());

10.提交4個使用迴圈計數器作為優先順序的任務給執行者。使用execute()方法提交這些任務給執行者。

for (int i=0; i<4; i++){
MyPriorityTask task=new MyPriorityTask ("Task "+i,i);
executor.execute(task);
}

11.令當前執行緒睡眠1秒。

try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}

12.提交4個額外的,使用迴圈計數器作為優先順序的任務給執行者。使用execute()方法提交這些任務給執行者。

for (int i=4; i<8; i++) {
MyPriorityTask task=new MyPriorityTask ("Task "+i,i);
executor.execute(task);
}

13.使用shutdown()方法關閉這個執行者。

executor.shutdown();

14.使用awaitTermination()方法等待這個執行者的結束。

try {
executor.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
}

15.寫入一條資訊表明這個程式的結束。

System.out.printf("Main: End of the program.\n");

它是如何工作的…

很容易將執行者轉換成一個基於優先順序的(執行者)。你只要傳入一個引數化為Runnable介面的PriorityBlockingQueue物件作為引數。但是,使用執行者時,你應該知道儲存在優先順序列隊中的所有物件必須實現Comparable介面。

你已經實現了MyPriorityTask類,(作為一個任務)它實現了Runnable介面和Comparable介面,它被儲存在優先順序佇列中。這個類有一個Priority屬性,用來儲存任務的優先順序。如果一個任務的這個屬性有更高的值,它將被更早的執行。compareTo()方法決定任務在優先順序列隊中的順序。在Main類,你提交8個不同優先順序的任務給執行者。你提交給執行者的第一個任務將第一個被執行。由於執行者閒置的,正在等待任務被執行,當第一個任務到達執行者時,執行者立即執行它們。你已經建立有2個執行執行緒的執行者,所以,前兩個任務將第一個被執行。然後,剩下的任務將按它們的優先順序來執行。

以下截圖顯示了示例的一次執行:

1

不止這些…

你可以使用任何實現BlockingQueue介面(的佇列)來配置執行者。DelayQueue是一個有趣的實現。這個類被用來儲存延遲啟用(delayed activation)的元素。它提供只返回活動物件的方法。你可以使用這個類來實現自己版本的ScheduledThreadPoolExecutor類。

參見