当番石榴表的后备映射是线程安全的时,其线程是否安全?

2022-09-03 04:01:31

Guava的Tables.newCustomTable(Map,Supplier)方法在提供线程安全映射时会返回线程安全表吗?例如:

public static <R, C, V> Table<R, C, V> newConcurrentTable() {
  return Tables.newCustomTable(
      new ConcurrentHashMap<R, Map<C, V>>(),
      new Supplier<Map<C, V>>() {
        public Map<C, V> get() {
          return new ConcurrentHashMap<C, V>();
        }
      });
}

该代码是否实际返回并发表?


答案 1

来自 doc:“如果多个线程同时访问此表,并且其中一个线程修改了该表,则必须在外部同步该表。

并发支持集合是不够的。


答案 2

Kevin Bourrillion是对的。您构建的映射不是线程安全的技术原因是,即使您使用的映射是线程安全的,表操作也可能不是线程安全的。让我举一个 put 的例子,如 中实现的那样,它由 :StandardTableTables.newCustomTable

public V put(R rowKey, C columnKey, V value) {
  Map<C, V> map = backingMap.get(rowKey);
  if (map == null) {
    map = factory.get();
    backingMap.put(rowKey, map);
  }
  return map.put(columnKey, value);
}

螺纹安全在处理外壳时受到损害。也就是说,两个或多个线程可以进入该块并为 创建一个新条目,而最后一个执行该条目最终将覆盖 in 中的条目,这将导致其他线程执行的操作丢失。特别是,在多线程环境中执行此操作的结果是非确定性的,这相当于说此操作不是线程安全的。map == nullcolumnKeybackingMap.put(rowKey, map)columnKeybackingMapput

此方法的正确实现是:

public V put(R rowKey, C columnKey, V value) {
    ConcurrentMap<C, V> map = table.get(rowKey);
    if (map == null) {
        backingMap.putIfAbsent(rowKey, factory.get());
    }
    map = backingMap.get(rowKey);
    return map.put(columnKey, value);
}

我目前正在调查是否有可能将实现与您想要执行的操作一起使用,以获得适当的线程安全 。ForwardingTableConcurrentTable

但说实话,我认为没有线程安全实现的原因是接口本身不提供任何并发构造,例如 或 .TableputIfAbsentreplace


推荐