当 Guava Table 的支持映射是线程安全的时,它的线程安全吗?

发布于 2024-12-13 16:55:08 字数 640 浏览 0 评论 0原文

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

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>();
        }
      });
}

该代码实际上返回并发表吗?

Will Guava's Tables.newCustomTable(Map, Supplier) method return thread safe tables when supplied with thread safe maps? For example:

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>();
        }
      });
}

Does that code actually return concurrent tables?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

愁以何悠 2024-12-20 16:55:08

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

并发支持集合还不够。

From the doc: "If multiple threads access this table concurrently and one of the threads modifies the table, it must be synchronized externally."

Concurrent backing collections aren't enough.

北恋 2024-12-20 16:55:08

凯文·伯里利恩是对的。您构造的映射不是线程安全的技术原因是,即使您使用的映射是线程安全的,表操作也可能不是。让我举一个 put 的例子,它是在 StandardTable 中实现的,它是由 Tables.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);
}

在处理 map = 时,线程安全受到损害。 = null 情况。也就是说,两个或多个线程可以进入该块并为 columnKey 创建一个新条目,最后一个执行 backingMap.put(rowKey, map) 最终将覆盖backingMapcolumnKey 的条目,这会导致其他线程执行的 put 操作丢失。特别是在多线程环境下这个操作的结果是不确定的,相当于说这个操作不是线程安全的。

此方法的正确实现是:

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);
}

我目前正在调查是否可以将 ForwardingTable 实现与您想要执行的操作一起使用,以获得正确的线程安全 ConcurrentTable

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

Kevin Bourrillion is right. The technical reason for the map you've constructed not to be thread safe is that even if the maps you are using are thread safe, the table operations may not be. Let me give an example of put, as implemented in the StandardTable, which is used by Tables.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);
}

Thread safety is compromised in the handling of the map == null case. Namely, two or more threads could enter that block and create a new entry for the columnKey and the last one to perform a backingMap.put(rowKey, map) would ultimately override the entry for the columnKey in the backingMap, which would lead to the loss of put operations performed by other threads. In particular the result of this operation in a multithreaded environment is non-deterministic, which is equivalent to saying that this operation is not thread safe.

The correct implementation of this method would be:

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);
}

I'm currently investigating if it is possible to use the ForwardingTable implementation together with what you've wanted to do, to get a properly thread safe ConcurrentTable.

But to be honest, I think the reason there is no thread-safe implementation of the Table is that the interface itself doesn't provide any concurrency constructs, such as putIfAbsent or replace.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文