【问题标题】:Implementing per-key or striped locking in a Map - best approach?在 Map 中实现每个键或条带锁定 - 最佳方法?
【发布时间】:2014-01-13 02:10:45
【问题描述】:

我在工作中遇到了这个困境,想看看是否有更好的解决方案……感觉应该有一个更简单、更清晰的答案。

目标:在键级别而不是整个映射级别同时访问带有锁的映射,以确保原子性同时尽可能少地影响性能。

我有一个需要并发的 Map。 *(添加)随着时间的推移,地图将充满未知数量的条目。我有多个读者和一个作家。 writer 执行“check-then-put”,而 reader 执行简单的 get()。我需要这些是原子的……但仅限于关键级别。因此,例如,如果阅读器正在检查密钥 X,而作者正在写入密钥 Y,我不在乎是否错过了对密钥 Y 的写入。如果阅读器/编写器正在使用相同的密钥,但我需要那是原子的。

最简单的解决方案是锁定整个地图。但这似乎会影响性能,因为地图中最终会出现大约 10,000 个键。 (如果这看起来不会因为 Map 的大小相对较小而影响性能,那么我们假设 Map 有更多的键,为了争论。)

据我所知,ConcurrentHashMap 不能保证我需要的“每个键”原子行为。

想到的下一个解决方案是拥有一组锁对象。您将根据原始密钥的散列索引到锁定Object() 的数组。这仍然会有一些争论,因为您拥有的锁少于原始地图中的钥匙。我知道 ConcurrentHashMap 在后台做了类似的事情(条带化)以提供并发性(但不是原子性)。

有没有更简单的方法来执行这种类型的按键或条带锁定?

谢谢。

【问题讨论】:

  • 是否提前知道所有可能的键?如果是这样,您可以在生成读取器和写入器之前为每个键分配一个锁对象。
  • @AdamBliss 我们并不提前知道所有的密钥。另外,如果我可以避免维护自己的锁对象 Map,那将是一个理想的解决方案。
  • 究竟您需要哪些操作是每个键的原子操作? ConcurrentHashMap 应该在这里做正确的事。例如ConcurrentHashMap.put 是原子的。
  • @LouisWasserman 我只关注推杆和接球。对于 put,我正在做一个 check-then-put,所以它不仅仅是对 put() 的调用。也许 ConcurrentHashMap 可以使用 putIfAbsent() 为我做到这一点?
  • 没问题,是的。

标签: java multithreading map concurrency java-6


【解决方案1】:

当价值生成是一个耗时的过程时,就会出现这种问题。您不想锁定整个地图并找到缺失值,并在生成值时保持地图锁定。您可以在生成期间释放地图,但随后您可能会同时发生两次未命中和生成。

与其直接将值与键一起存储,不如将其存储在引用对象中:

public class Ref<T>
{
    private T value;

    public T getValue()
    {
        return value;
    }

    public void setValue(T value)
    {
        this.value = value;
    }
}

因此,如果您最初拥有Map&lt;String, MyThing&gt; 的地图,则改为使用Map&lt;String, Ref&lt;MyThing&gt;&gt;。不要为并发实现而烦恼,只需使用 HashMap 或 LinkedHashMap 或其他。

现在您可以锁定地图以查找或创建参考持有人,然后释放地图。之后,您可以锁定引用以查找或创建值对象:

String key; // key you're looking up
Map<String, Ref<MyThing>> map; // the map

// Find the reference container, create it if necessary
Ref<MyThing> ref;
synchronized(map)
{
    ref = map.get(key);
    if (ref == null)
    {
        ref = new Ref<MyThing>();
        map.put(key, ref);
    }
}

// Map is released at this point

// Now get the value, creating if necessary
MyThing result;
synchronized(ref)
{
    result = ref.getValue();
    if (result == null)
    {
        result = generateMyThing();
        ref.setValue(result);
    }
}

// result == your existing or new object

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-23
    • 1970-01-01
    • 2022-08-24
    • 2023-03-24
    • 1970-01-01
    • 2012-04-09
    • 2023-03-23
    相关资源
    最近更新 更多