8.4 改进的ConcurrentHashMap

引入ConcurrentHashMap类是为了提供一个更加现代的HashMap,以更好地应对高并发的场景。ConcurrentHashMap允许执行并发的添加和更新操作,其内部实现基于分段锁。与另一种解决方案——同步式的Hashtable相比较,ConcurrentHashMap的读写性能都更好(注意,标准的HashMap是不带同步的)。

8.4.1 归约和搜索

ConcurrentHashMap类支持三种新的操作,让我们回忆一下在流中学习到的内容:

  • forEach——对每个(键, 值)对执行指定的操作;
  • reduce——依据归约函数整合所有(键, 值)对的计算结果;
  • search——对每个(键, 值)对执行一个函数,直到函数取得一个非空值。

每种操作支持四种形式的参数,接受函数使用键、值、Map.Entry以及(键, 值)对作为参数:

  • 使用键(forEachKeyreduceKeyssearchKeys);
  • 使用值(forEachValuereduceValuessearchValues);
  • 使用Map.Entry对象(forEachEntryreduceEntriessearchEntries);
  • 使用键和值(forEachreducesearch)。

注意,所有这些操作都不会对ConcurrentHashMap的状态上锁,它们只是在运行中动态地对对象加锁。执行操作的函数不应对执行顺序或其他对象或可能在运行中变化的值有任何的依赖。

此外,你还需要为所有操作设定一个并行阈值。如果当前Map的规模比指定的阈值小,方法就只能顺序执行。使用通用线程池时,如果把并行阈值设置为1将获得最大的并行度。将阈值设定为Long.MAX_VALUE时,方法将以单线程的方式运行。除非你的软件架构经过高度的资源优化,否则通常情况下,建议你遵守这些原则。

接下来的这个例子使用reduceValues方法来获取Map的最大值:

  1. ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>(); ←---- 一个可能有多个键和值更新的ConcurrentHashMap对象
  2. long parallelismThreshold = 1;
  3. Optional<Integer> maxValue =
  4. Optional.ofNullable(map.reduceValues(parallelismThreshold, Long::max));

请留意,intlongdouble等基础类型的归约操作(reduceValuesToIntreduceKeysToLong等)会更加高效,因为它们没有额外的封装开销。

8.4.2 计数

ConcurrentHashMap类提供了一个新的mappingCount方法,能以长整形long返回Map中的映射数目。你应该尽量在新的代码中使用它,而不是继续使用返回intsize方法。这样做能让你的代码更具扩展性,更好地适应将来的需要,因为总有一天Map中映射的数目可能会超过int能表示的范畴。

8.4.3 Set视图

ConcurrentHashMap类还提供了一个新的keySet方法,该方法以Set的形式返回ConcurrentHashMap的一个视图(Map中的变化会反映在返回的Set中,反之亦然)。你也可以使用新的静态方法newKeySet创建一个由ConcurrentHashMap构成的Set