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是线程安全的的集合。其中所有的修改线程对底层数组进行复制。如果在集合上进行迭代的线程数超过修改线程数,这样的安排是很有用的。当构建一个迭代器的时候,它包含一个当前数组的引用。如果数组后来被修改了,迭代器仍然引用旧数组,但是,集合的数组已经被替换了。因而,旧的迭代器拥有一致的(可能过时的)视图,访问它无须同步开销。

猜你喜欢

转载自blog.csdn.net/qq_38386085/article/details/85224429