併發集合(五)使用執行緒安全的、帶有延遲元素的列表
宣告:本文是《 Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González 譯者:許巧輝 校對:方騰飛
使用執行緒安全的、帶有延遲元素的列表
DelayedQueue類是Java API提供的一種有趣的資料結構,並且你可以用在併發應用程式中。在這個類中,你可以儲存帶有啟用日期的元素。方法返回或抽取佇列的元素將忽略未到期的資料元素。它們對這些方法來說是看不見的。
為了獲取這種行為,你想要儲存到DelayedQueue類中的元素必須實現Delayed介面。這個介面允許你處理延遲物件,所以你將實現儲存在DelayedQueue物件的啟用日期,這個啟用時期將作為物件的剩餘時間,直到啟用日期到來。這個介面強制實現以下兩種方法:
- compareTo(Delayed o):Delayed介面繼承Comparable介面。如果執行這個方法的物件的延期小於作為引數傳入的物件時,該方法返回一個小於0的值。如果執行這個方法的物件的延期大於作為引數傳入的物件時,該方法返回一個大於0的值。如果這兩個物件有相同的延期,該方法返回0。
- getDelay(TimeUnit unit):該方法返回與此物件相關的剩餘延遲時間,以給定的時間單位表示。TimeUnit類是一個列舉類,有以下常量:DAYS、HOURS、 MICROSECONDS、MILLISECONDS、 MINUTES、 NANOSECONDS 和 SECONDS。
在這個例子中,你將學習如何使用DelayedQueue類來儲存一些具有不同啟用日期的事件。
準備工作…
這個指南的例子使用Eclipse IDE實現。如果你使用Eclipse或其他IDE,如NetBeans,開啟它並建立一個新的Java專案。
如何做…
按以下步驟來實現的這個例子:
1.建立一個實現Delayed介面的Event類。
public class Event implements Delayed
2.宣告一個私有的、Date型別的屬性startDate。
private Date startDate;
3.實現這個類的構造器,並初始化它的屬性。
public Event (Date startDate) {this.startDate=startDate;}
4.實現compareTo()方法。它接收一個Delayed物件作為引數。返回當前物件的延期與作為引數傳入物件的延期之間的差異。
<br /><br />@Override<br />public int compareTo(Delayed o) {<br />long result=this.getDelay(TimeUnit.NANOSECONDS)-o.<br />getDelay(TimeUnit.NANOSECONDS);<br />if (result<0) {<br />return -1;<br />} else if (result>0) {<br />return 1;<br />}<br />return 0;<br />}<br /><br />
5.實現getDelay()方法。返回物件的startDate與作為引數接收的TimeUnit的真實日期之間的差異。
public long getDelay(TimeUnit unit) { Date now=new Date(); long diff=startDate.getTime()-now.getTime(); return unit.convert(diff,TimeUnit.MILLISECONDS); }
6.建立一個實現Runnable介面的Task類。
public class Task implements Runnable {
7.宣告一個私有的、int型別的屬性id,用來儲存任務的標識數字。
private int id;
8.宣告一個私有的、引數化為Event類的DelayQueue型別的屬性queue。
<br /><br />private DelayQueue<Event> queue;<br /><br />
9.實現這個類的構造器,並初始化它的屬性。
public Task(int id, DelayQueue<Event> queue) { this.id=id;<br />this.queue=queue; }
10.實現run()方法。首先,計算任務將要建立的事件的啟用日期。新增等於物件ID的實際日期秒數。
@Override public void run() { Date now=new Date(); Date delay=new Date(); delay.setTime(now.getTime()+(id*1000)); System.out.printf("Thread %s: %s\n",id,delay);
11.使用add()方法,在佇列中儲存100個事件。
for (int i=0; i<100; i++) { Event event=new Event(delay); queue.add(event); }
12.通過建立Main類,並實現main()方法,來實現這個例子的主類。
public class Main { public static void main(String[] args) throws Exception {
13.建立一個引數化為Event類的DelayedQueue物件。
DelayQueue<Event> queue=new DelayQueue<>();
14.建立一個有5個Thread物件的陣列,用來儲存將要執行的任務。
Thread threads[]=new Thread[5];
15.建立5個具有不同IDs的Task物件。
for (int i=0; i<threads.length; i++){<br />Task task=new Task(i+1, queue);<br />threads[i]=new Thread(task); }
16.開始執行前面建立的5個任務。
for (int i=0; i<threads.length; i++) { threads[i].start(); }
17.使用join()方法等待任務的結束。
for (int i=0; i<threads.length; i++) { threads[i].join(); }
18.將儲存在佇列中的事件寫入到控制檯。當佇列的大小大於0時,使用poll()方法獲取一個Event類。如果它返回null,令主執行緒睡眠500毫秒,等待更多事件的啟用。
do { int counter=0; Event event; do { event=queue.poll(); if (event!=null) counter++; } while (event!=null); System.out.printf("At %s you have read %d events\n",new Date(),counter); TimeUnit.MILLISECONDS.sleep(500); }while (queue.size()>0); } }
它是如何工作的…
在這個指南中,我們已實現Event類。這個類只有一個屬性(表示事件的啟用日期),實現了Delayed介面,所以,你可以在DelayedQueue類中儲存Event物件。
getDelay()方法返回在實際日期和啟用日期之間的納秒數。這兩個日期都是Date類的物件。你已使用getTime()方法返回一個被轉換成毫秒的日期,你已轉換那個值為作為引數接收的TimeUnit。DelayedQueue類使用納秒工作,但這一點對於你來說是透明的。
對於compareTo()方法,如果執行這個方法的物件的延期小於作為引數傳入的物件的延期,該方法返回小於0的值。如果執行這個方法的物件的延期大於作為引數傳入的物件的延期,該方法返回大於0的值。如果這兩個物件的延期相等,則返回0。
你同時實現了Task類。這個類有一個整數屬性id。當一個Task物件被執行,它增加一個等於任務ID的秒數作為實際日期,這是被這個任務儲存在DelayedQueue類的事件的啟用日期。每個Task物件使用add()方法儲存100個事件到佇列中。
最後,在Main類的main()方法中,你已建立5個Task物件,並用相應的執行緒來執行它們。當這些執行緒完成它們的執行,你已使用poll()方法將所有元素寫入到控制檯。這個方法檢索並刪除佇列的第一個元素。如果佇列中沒有任務到期的元素,這個方法返回null值。你呼叫poll()方法,並且如果它返回一個Evnet類,你增加計數器。當poll()方法返回null值時,你寫入計數器的值到控制檯,並且令執行緒睡眠半秒等待更多的啟用事件。當你獲取儲存在佇列中的500個事件,這個程式執行結束。
以下截圖顯示程式執行的部分輸出:
你可以看出這個程式當它被啟用時,只獲取100個事件。
注意:你必須十分小心size()方法。它返回列表中的所有元素數量,包含啟用與未啟用元素。
不止這些…
DelayQueue類提供其他有趣方法,如下:
- clear():這個方法刪除佇列中的所有元素。
- offer(E e):E是代表用來引數化DelayQueue類的類。這個方法插入作為引數傳入的元素到佇列中。
- peek():這個方法檢索,但不刪除佇列的第一個元素。
- take():這具方法檢索並刪除佇列的第一個元素。如果佇列中沒有任何啟用的元素,執行這個方法的執行緒將被阻塞,直到佇列有一些啟用的元素。
參見