1. 程式人生 > >Java併發——執行緒安全的集合(二)

Java併發——執行緒安全的集合(二)

1.對併發雜湊對映的批操作:

Java SE 8為併發雜湊對映提供了批操作,即使有其他執行緒在處理對映,這些操作也能安全地執行。批操作會遍歷對映,處理遍歷過程中找到的元素。無須凍結當前對映的快照。

有三種不同的批操作:搜尋、歸約、forEach。

每個操作都有四個版本:operationKeys(處理鍵)、operationValues(處理值)、operation(處理鍵和值)、operationEntries(處理Map.Entry物件)。

對於上述的各個操作,需要指定一個引數化閥值(operation threshold)。如果對映包含的元素多於這個閥值,就會並行完成批操作。例如搜尋方法:

U searchKeys(long threshold, BiFunction<? super K, ? extends U> f)
U searchValues(long threshold, BiFunction<? super K, ? extends U> f)
U search(long threshold, BiFunction<? super K, ? super V, ? extends U> f)
U searchEntries(long threshold, BiFunction<MapEntry<K, V>, ? extends U> f)

假如我們需要找出第一個出現1000次的單詞:

String result = map.search(threshold, (k, v) -> v > 1000 ? k : null);

forEach操作:

map.forEach(threshold, (k, v) -> System.out.println(k + "->" + v));
//另一種方式:轉換器函式
map.forEach(threshold, 
               (k, v) -> k + "->" + v, //轉換器
               System.out::println);

reduce操作:

Long sum = map.reduceValues(threshold, Long::sum);
//另一種方式:轉換器函式
Integer maxlength = map.reduceKeys(threshold, 
                                        String::length, //轉換器
                                        Integer::max); 
//如果對映為空,或者所有條目都被過濾掉,reduce操作會返回null。如果只有一個元素,則返回其轉換結果,不會應用累加器。

2.併發集檢視:

並不存在一個ConcurrentHashSet類,如果需要一個大的執行緒安全的集,可以通過ConcurrentHashMap靜態的newKeySet方法得到一個Set<K>,這實際上是ConcurrentHashMap<K, Boolean>的一個包裝器。(所有對映值都為Boolean.TRUE,但是並不關心其值是什麼)。

Set<String> words = ConcurrentHashMap.<String>newKeySet();

這個集是可變的,如果刪除這個集的元素,這個鍵會從對映中刪除。但是不能插入元素,因為沒有相應的值可以增加。Java SE 8為ConcurrentHashMap增加了第二個keySet方法,包含一個預設值,可以在為集增加元素時使用:

Set<String> words = map.keySet(1L);
words.add("Java");

如果"Java"在words中不存在,現在他會有一個值1。

3.寫陣列的拷貝:

CopyOnWriteArrayList和CopyOnWriteArraySet是執行緒安全的的集合。其中所有的修改執行緒對底層陣列進行復制。如果在集合上進行迭代的執行緒數超過修改執行緒數,這樣的安排是很有用的。當構建一個迭代器的時候,它包含一個當前陣列的引用。如果陣列後來被修改了,迭代器仍然引用舊陣列,但是,集合的陣列已經被替換了。因而,舊的迭代器擁有一致的(可能過時的)檢視,訪問它無須同步開銷。