1. 程式人生 > >併發集合(六)使用執行緒安全的NavigableMap

併發集合(六)使用執行緒安全的NavigableMap

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

使用執行緒安全的NavigableMap

Java API 提供的有趣的資料結構,並且你可以在併發應用程式中使用,它就是ConcurrentNavigableMap介面的定義。實現ConcurrentNavigableMap介面的類儲存以下兩部分元素:

  • 唯一標識元素的key
  • 定義元素的剩餘資料

每部分在不同的類中實現。

Java API 也提供了這個介面的實現類,這個類是ConcurrentSkipListMap,它實現了非阻塞列表且擁有ConcurrentNavigableMap的行為。在內部實現中,它使用Skip List來儲存資料。Skip List是基於並行列表的資料結構,它允許我們獲取類似二叉樹的效率。使用它,你可以得到一個排序的資料結構,這比排序數列使用更短的訪問時間來插入、搜尋和刪除元素。

注意:在1990年,由William Pugh引入Skip List。

當你往map中插入資料時,它使用key來排序它們,所以,所有元素將是有序的。除了返回具體的元素,這個類也提供了獲取map的子map的方法。

在這個指南中,你將學習如何使用ConcurrentSkipListMap類來實現一個通訊錄的map。

準備工作…

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

如何做…

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

1.建立一個Contact類。

public class Contact {

2.宣告兩個私有的、String型別的屬性name和phone。

private String name;
private String phone;

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

public Contact(String name, String phone) {
this.name=name;
this.phone=phone;
}

4.實現返回name和phone屬性值的方法。

public String getName() {
return name;
}
public String getPhone() {
return phone;
}

5.建立一個Task類,並指定它實現Runnable介面。

public class Task implements Runnable {

6.宣告一個私有的、引數化為String類和Contact類的ConcurrentSkipListMap型別的屬性map。

private ConcurrentSkipListMap<String, Contact> map;

7.宣告一個私有的、String型別的屬性id,用來儲存當前任務的ID。

private String id;

[/code[

8.實現這個類的構造器,用來儲存它的屬性。



public Task (ConcurrentSkipListMap<String, Contact> map, String
id) {
this.id=id;
this.map=map;
}

9.實現run()方法。使用任務的ID和建立Contact物件的增長數,在map中儲存1000個不同的通訊錄。使用put()方法新增通訊錄到map中。

@Override
public void run() {
for (int i=0; i<1000; i++) {
Contact contact=new Contact(id, String.valueOf(i+1000));
map.put(id+contact.getPhone(), contact);
}
}

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

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

11.建立一個引數化為String類和Contact類的ConcurrentSkipListMap物件map。

ConcurrentSkipListMap<String, Contact> map;
map=new ConcurrentSkipListMap<>();

12.建立一個有25個Thread物件的陣列,用來儲存你將要執行的所有任務。

Thread threads[]=new Thread[25];
int counter=0;

13.建立和啟動25個任務,對於每個任務指定一個大寫字母作為ID。

for (char i='A'; i<'Z'; i++) {
Task task=new Task(map, String.valueOf(i));
threads[counter]=new Thread(task);
threads[counter].start();
counter++;
}

14.使用join()方法等待執行緒的結束。

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

15.使用firstEntry()方法獲取map的第一個實體,並將它的資料寫入到控制檯。

System.out.printf("Main: Size of the map: %d\n",map.size());
Map.Entry<String, Contact> element;
Contact contact;
element=map.firstEntry();
contact=element.getValue();
System.out.printf("Main: First Entry: %s: %s\n",contact.
getName(),contact.getPhone());

16.使用lastEntry()方法獲取map的最後一個實體,並將它的資料寫入到控制檯。

element=map.lastEntry();
contact=element.getValue();
System.out.printf("Main: Last Entry: %s: %s\n",contact.
getName(),contact.getPhone());

17.使用subMap()方法獲取map的子map,並將它們的資料寫入到控制檯。

System.out.printf("Main: Submap from A1996 to B1002: \n");
ConcurrentNavigableMap<String, Contact> submap=map.
subMap("A1996", "B1002");
do {
element=submap.pollFirstEntry();
if (element!=null) {
contact=element.getValue();
System.out.printf("%s: %s\n",contact.getName(),contact.
getPhone());
}
} while (element!=null);
}

它是如何工作的...

在這個指南中,我們已實現Task類來儲存Contact物件到NavigableMap 中。每個通訊錄都有一個名稱(建立它的任務的ID的)和電話號碼(1000到2000之間的數字)。我們已使用這些值的連續值作為通訊錄的key。每個Task物件建立1000個通訊錄,並使用put()方法將它們儲存到NavigableMap中。

注意:如果你插入的key已存在,那麼這個key的元素將被新的元素取代。

Main類的main()方法建立25個Task物件,並使用A-Z的字母作為IDs。然後,你已使用一些方法從map中獲取資料。firstEntry()方法返回map第一個元素的Map.Entry物件,且不會刪除這個元素。這個物件包含key和元素。你已呼叫getValue()方法來獲取元素。你可以使用getKey()來獲取元素的key。

lastEntry()方法返回map最後一個元素的Map.Entry物件,subMap()方法返回map的部分元素的ConcurrentNavigableMap物件。在這個例子中,元素擁有A1996到B1002之間的key。在這種情況下,你可以使用pollFirst()方法來處理subMap()方法返回的這些元素。這個方法將返回並刪除submap中的第一個Map.Entry物件。

以下截圖顯示了程式執行的輸出:

4

不止這些...

ConcurrentSkipListMap類有其他有趣的方法,這些方法如下:

  • headMap(K toKey):K是引數化ConcurrentSkipListMap物件的Key值的類。返回此對映的部分檢視,其鍵值小於 toKey
  • tailMap(K fromKey):K是引數化ConcurrentSkipListMap物件的Key值的類。返回此對映的部分檢視,其鍵大於等於 fromKey
  • putIfAbsent(K key, V Value):如果key不存在map中,則這個方法插入指定的key和value。
  • pollLastEntry():這個方法返回並刪除map中最後一個元素的Map.Entry物件。
  • replace(K key, V Value):如果這個key存在map中,則這個方法將指定key的value替換成新的value。

參見

  • 在第6章,併發集合中的使用非阻塞執行緒安全的數列指南