Java併發50:併發集合系列-基於獨佔鎖實現的雙向阻塞佇列LinkedBlockingDeque
原文地址:http://ifeve.com/concurrent-collections-3/
關於與LinkedBlockingDeque類似的單向佇列LinkedBlockingQueue可以參考:Java併發49
使用阻塞執行緒安全的列表
列表(list)是最基本的集合。
一個列表中的元素數量是不確定的,並且你可以新增、讀取和刪除任意位置上的元素。
併發列表允許不同的執行緒在同一時刻對列表裡的元素進行新增或刪除,而不會產生任何資料不一致的問題。
在這個指南中,你將學習如何在你的併發應用程式中使用阻塞的列表。
阻塞列表與非阻塞列表的主要區別是,阻塞列表有新增和刪除元素的方法,如果由於列表已滿或為空而導致這些操作不能立即進行,它們將阻塞呼叫的執行緒,直到這些操作可以進行。
Java包含實現阻塞列表的LinkedBlockingDeque類。
你將使用以下兩種任務來實現例子:
- 新增大量資料到列表。
- 從同一個列表中刪除大量的資料。
準備工作…
這個指南的例子使用Eclipse IDE實現。如果你使用Eclipse或其他IDE,如NetBeans,開啟它並建立一個新的Java專案。
如何做…
按以下步驟來實現的這個例子:
1.建立一個實現Runnable介面的Client類。
public class Client implements Runnable{
2.宣告一個私有的、LinkedBlockingDeque型別的、引數化為String類的屬性requestList。
private LinkedBlockingDeque requestList;
3.實現這個類的構造器,並初始化它的屬性。
public Client (LinkedBlockingDeque requestList) { this.requestList=requestList; }
4.實現run()方法。使用requestList物件的put()方法,每秒往列表插入5個String物件。重複這個迴圈3次。
@Override public void run() { for (int i=0; i<3; i++) { for (int j=0; j<5; j++) { StringBuilder request=new StringBuilder(); request.append(i); request.append(":"); request.append(j); try { requestList.put(request.toString()); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("Client: %s at %s.\n",request,new Date()); } try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.printf("Client: End.\n"); }
5.建立這個例子的主類,通過實現Main類,並實現main()方法。
public class Main { public static void main(String[] args) throws Exception {
6.宣告和建立引數化為String類、名為list的LinkedBlockingDeque。
LinkedBlockingDeque list=new LinkedBlockingDeque(3);
7.建立和啟動一個Thread物件來執行client任務。
Client client=new Client(list); Thread thread=new Thread(client); thread.start();
8.使用這個列表的take()方法,每300毫秒獲取列表的3個字串(String)物件。重複這個迴圈5次。將字串(String)寫入到控制檯。
for (int i=0; ifor (int j=0; j<3; j++) { String request=list.take(); System.out.printf("Main: Request: %s at %s. Size:%d\n",request,new Date(),list.size()); } TimeUnit.MILLISECONDS.sleep(300);
9.寫入一條資訊表明程式的結束。
System.out.printf("Main: End of the program.\n");
它是如何工作的…
在這個指南中,你已使用引數化為String類的LinkedBlockingDeque來處理非阻塞併發列表的資料。
Client類使用put()方法新增字串到列表中。如果列表已滿(因為你已使用固定大小來建立它),這個方法阻塞執行緒的執行,直到列表有可用空間。
Main類使用take()方法從列表中獲取字串,如果列表為空,這個方法將阻塞執行緒的執行,直到列表中有元素。
在這個例子中,使用LinkedBlockingDeque類的這兩個方法,如果它們在阻塞時被中斷,將丟擲InterruptedException異常。所以,你必須包含必要的程式碼來捕捉這個異常。
不止這些…
LinkedBlockingDeque類同時提供方法用於新增和獲取列表的元素,而不被阻塞,或丟擲異常,或返回null值。這些方法是:
- takeFirst() 和takeLast():這些方法分別返回列表的第一個和最後一個元素。它們從列表刪除返回的元素。如果列表為空,這些方法將阻塞執行緒,直到列表有元素。
- getFirst() 和getLast():這些方法分別返回列表的第一個和最後一個元素。它們不會從列表刪除返回的元素。如果列表為空,這些方法將丟擲NoSuchElementExcpetion異常。
- peek()、peekFirst(),和peekLast():這些方法分別返回列表的第一個和最後一個元素。它們不會從列表刪除返回的元素。如果列表為空,這些方法將返回null值。
- poll()、pollFirst()和 pollLast():這些方法分別返回列表的第一個和最後一個元素。它們從列表刪除返回的元素。如果列表為空,這些方法將返回null值。
- add()、 addFirst()、addLast():這些方法分別在第一個位置和最後一個位置上新增元素。如果列表已滿(你已使用固定大小建立它),這些方法將丟擲IllegalStateException異常。