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