2

如果地图还没有,我需要为地图添加一些价值。键-> 值(如果已设置)应始终位于两个集合中(即put应以原子方式发生在两个映射中)。我试图按如下方式实现这一点:

private final ConcurrentMap<String, Object> map1 = new ConcurrentHashMap<String, Object>();
private final ConcurrentMap<String, Object> map2 = new ConcurrentHashMap<String, Object>();

public Object putIfAbsent(String key) {
    Object retval = map1.get(key);
    if (retval == null) {
        synchronized (map1) {
            retval = map1.get(key);
            if (retval == null) {
                Object value = new Object(); //or get it somewhere
                synchronized (map2) {
                    map1.put(key, value);
                    map2.put(key, new Object());
                }
                retval = value;
            }
        }
    }
    return retval;
}

public void doSomething(String key) {
    Object obj1 = map1.get(key);
    Object obj2 = map2.get(key);
    //do smth
}

这在所有情况下都能正常工作吗?谢谢

4

4 回答 4

0

几个问题:

  • 不要使用“双重检查锁定”。快速的 Google 搜索将显示大量文章,这些文章解释了这种技术的问题。只需检查synchronized块内部。
  • 您不需要在map1和上同步map2。只需使用其中一种。
  • 内同步doSomething。确保在用于同步的同一对象上进行同步putIfAbsent
于 2012-10-08T21:34:52.933 回答
0

您永远不应该将同步与 ConcurrentHashMap 一起使用(这几乎违背了目的)。对 CHH 进行原子添加的最佳方法是使用内置的替换方法。例如:

do {

    oldValue1 = map1.get(key1);
    oldValue2 = map2.get(key2);
    newValue1 = // some logic to determine a new value for key1/value1
    newValue2 = // some more logic to determine a new value for key2/value2

} while (!map1.replace(key1, oldValue1, newValue1) && !map2.replace(key2, oldValue2, newValue2));

我不知道如何专门针对您的示例进行调整,但这应该会给您一个开始的地方。基本上发生的事情是您从地图中获取密钥,执行一些逻辑,如果密钥仍然与逻辑之前相同,它将替换条目并返回true,然后循环将中断。否则它只会重复循环,直到它可以原子地进行更新。

于 2012-10-08T21:38:56.053 回答
0

好的,我终于找到了这个解决方案:

private Map<String, Object> map1 = new HashMap<String, Object>();
private Map<String, Object> map2 = new HashMap<String, Object>();

private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

public void putIfAbsent(String key, Object o) {
    rwl.readLock().lock();
    try {
        if (map1.get(key) == null) {
            rwl.readLock().unlock();
            rwl.writeLock().lock();
            try {
                if (map1.get(key) == null) {
                    map1.put(key, getValue());
                    map2.put(key, getValue());
                }
            }finally {
                rwl.readLock().lock();
                rwl.writeLock().unlock();
            }
        }
    } finally {
        readLock.unlock();
    }
}

public void readMap(String key) {
   rwl.readLock().lock();
    try {
       Object obj1 = map1.get(key);
       Object obj2 = map2.get(key);
    } finally {
        rwl.readLock().unlock();
    }

}
于 2012-10-10T20:18:51.380 回答
-1

为了做你想做的事,我会使用原子引用:

class PairHolder {
   public final ConcurrentMap map1;
   public final ConcurrentMap map2;
   public PairHolder(...) // set values here.
}

private AtomicReference<PairHolder> mapHolder = ... // initialize it somehow

do {
  PairHolder holder = mapHolder.get();
  ConcurrentMap map1 = holder.map1.clone()
  ConcurrentMap map2 = holder.map2.clone()
  newMap1.putIfAbsent(...);
  newMap2.putIfAbsent(...);
} while (!mapHolder.compareAndSet(holder, new PairHolder(newMap1,newMap2))

这样你就可以确定,mapHolder 包含对 PairHolder 的引用,而这两个映射又以原子方式 100% 更新。至少 CAS 应该保证这一点,但是在多处理器系统上它可能是错误的。

于 2012-10-08T22:10:28.940 回答