並發集合(四)用優先級對使用阻塞線程安全的列錶排序

杜老師說 2022-01-07 14:42:25 阅读数:804

集合 使用 阻塞 安全 排序

聲明:本文是《 Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González     譯者:許巧輝 校對:方騰飛

用優先級對使用阻塞線程安全的列錶排序

一個典型的需求是,當你需要使用一個有序列錶的數據結構時,Java提供的PriorityBlockingQueue類就擁有這種功能。

你想要添加到PriorityBlockingQueue中的所有元素必須實現Comparable接口。這個接口有一個compareTo()方法,它接收同樣類型的對象,你有兩個比較的對象:一個是執行這個方法的對象,另一個是作為參數接收的對象。如果本地對象小於參數,則該方法返回小於0的數值。如果本地對象大於參數,則該方法返回大於0的數值。如果本地對象等於參數,則該方法返回等於0的數值。

PriorityBlockingQueue使用compareTo()方法决定插入元素的比特置。(校注:默認情况下)較大的元素將被放在隊列的尾部。

阻塞數據結構(blocking data structure)是PriorityBlockingQueue的另一個重要特性。它有這樣的方法,如果它們不能立即進行它們的操作,則阻塞這個線程直到它們的操作可以進行。

在這個指南中,你將學習如何使用PriorityBlockingQueue類實現一個例子,你將在相同的列錶上使用不同的優先級存儲大量事件(event),然後檢查隊列的排序是否是你想要的。

准備工作…

這個指南的例子使用Eclipse IDE實現。如果你使用Eclipse或其他IDE,如NetBeans,打開它並創建一個新的Java項目。

如何做…

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

1.實現Event類,並指定它實現參數化為Event類的Comparable接口。

public class Event implements Comparable<Event> {

2.聲明一個私有的、int類型的屬性thread,用來存儲已創建事件的線程數。

private int thread;

3.聲明一個私有的、int類型的屬性priority,用來存儲事件的優先級。

private int priority;

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

public Event(int thread, int priority){this.thread=thread;this.priority=priority;}

5.實現getThread()方法,用來返回thread屬性的值。

public int getThread() {return thread;}

6.實現getPriority()方法,用來返回priority屬性的值。

public int getPriority() {return priority;}

7.實現compareTo()方法。它接收Event作為參數,並且比較當前事件與參數的優先級。如果當前事件的優先級更大,則返回-1,如果這兩個優先級相等,則返回0,如果當前事件的優先級更小,則返回1。注意,這與大多數Comparator.compareTo()的實現是相反的。

@Overridepublic int compareTo(Event e) {if (this.priority>e.getPriority()) {return -1;} else if (this.priority<e.getPriority()) {return 1;} else {return 0;}}

8.創建一個Task類,並指定它實現Runnable接口。

public class Task implements Runnable {

9.聲明一個私有的、int類型的屬性id,用來存儲任務的數字標識。

private int id;

10.聲明一個私有的、參數化為Event類的PriorityBlockingQueue類型的屬性queue,用來存儲任務產生的事件。

private PriorityBlockingQueue<Event> queue;

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

public Task(int id, PriorityBlockingQueue<Event> queue) {this.id=id;this.queue=queue;}

12.實現run()方法。它存儲100個事件到隊列,使用它們的ID來標識創建事件的任務,並給予不斷增加的數作為優先級。使用add()方法添加事件到隊列中。

@Overridepublic void run() {for (int i=0; i<1000; i++){Event event=new Event(id,i);queue.add(event);}}

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

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

14.創建一個參數化為Event類的PriorityBlockingQueue對象。

PriorityBlockingQueue<Event> queue=new PriorityBlockingQueue<>();

15.創建一個有5個Thread對象的數組,用來存儲執行5個任務的線程。

Thread taskThreads[]=new Thread[5];

16.創建5個Task對象。存儲前面創建的線程數組。

for (int i=0; i<taskThreads.length; i++){Task task=new Task(i,queue);taskThreads[i]=new Thread(task);}

17.啟動前面創建的5個線程。

for (int i=0; i<taskThreads.length ; i++) {taskThreads[i].start();}

18.使用join()方法,等待這5個線程的結束。

for (int i=0; i<taskThreads.length ; i++) {try {taskThreads[i].join();} catch (InterruptedException e) {e.printStackTrace();}}

19.將列隊真實大小和存儲在它裏面的事件寫入到控制臺。使用poll()方法從隊列中取出事件。

System.out.printf("Main: Queue Size: %d\n",queue.size());for (int i=0; i<taskThreads.length*1000; i++){Event event=queue.poll();System.out.printf("Thread %s: Priority %d\n",event.getThread(),event.getPriority());}

20.將隊列最後的大小寫入到控制臺。

System.out.printf("Main: Queue Size: %d\n",queue.size());System.out.printf("Main: End of the program\n");

它是如何工作的…

在這個指南中,你已使用PriorityBlockingQueue實現Event對象的一個優先級隊列。正如我們在引言中提到的,所有存儲在PriorityBlockingQueue的元素必須實現Comparable接口,所以,你已在Event類中實現compareTo()方法。

所有事件都有一個優先級屬性。擁有更高優先級的元素將成為隊列的第一個元素。當你已實現compareTo()方法,如果執行這個方法的事件擁有比作為參數傳入的事件更高的優先級時,它將返回-1。在其他情况下,如果執行這個方法的事件擁有比作為參數傳入的事件更低的優先級時,它將返回1。如果這兩個對象擁有相同優先級,compareTo()方法將返回0。在這種情况下,PriorityBlockingQueue類並不能保證元素的順序。

我們已實現Task類來添加Event對象到優先級隊列中。每個任務對象使用add()方法往隊列添加1000個事件(0到99種優先級)。

Main類的main()方法創建5個Task對象,並用相應的線程執行它們。當所有的線程完成它們的執行,你已將所有元素寫入到控制臺。我們使用poll()方法從隊列中獲取元素。這個方法返回並删除隊列的第一個元素。

以下截圖顯示執行這個程序的部分輸出:

2

你可以看出這個隊列如何有5000個元素,第一個元素如何擁有最大的優先級值。

不止這些…

PriorityBlockingQueue類提供其他有趣的方法,以下是其中一些方法的描述:

  • clear():這個方法删除隊列中的所有元素。
  • take():這個方法返回並删除隊列中的第一個元素。如果隊列是空的,這個方法將阻塞線程直到隊列有元素。
  • put(E e):E是用來參數化PriorityBlockingQueue類的類。這個方法將作為參數傳入的元素插入到隊列中。
  • peek():這個方法返回列隊的第一個元素,但不删除它。

參見

FavoriteLoading添加本文到我的收藏
版权声明:本文为[杜老師說]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/01/202201071442248800.html