定製併發類(三)實現一個基於優先順序的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個執行執行緒的執行者,所以,前兩個任務將第一個被執行。然後,剩下的任務將按它們的優先順序來執行。
以下截圖顯示了示例的一次執行:
不止這些…
你可以使用任何實現BlockingQueue介面(的佇列)來配置執行者。DelayQueue是一個有趣的實現。這個類被用來儲存延遲啟用(delayed activation)的元素。它提供只返回活動物件的方法。你可以使用這個類來實現自己版本的ScheduledThreadPoolExecutor類。
參見