我正在研究的项目有很多用于ConcurrentHashMap的包装类,并且以(我认为)不正确的方式使用锁。它是否正确?

时间:2014-03-03 13:00:03

标签: java concurrency thread-safety concurrenthashmap

我必须使用许多用于在ConcurrentHashMap中存储数据的类,通常如下所示:

import java.util.concurrent.ConcurrentHashMap;

import java.util.concurrent.locks.ReentrantLock;

public class Data {
    private ConcurrentHashMap<String, SomeObject> concurrentHashMap = new ConcurrentHashMap<String, SomeObject>();
    private ReentrantLock lock = new ReentrantLock();

    public void add(String string, SomeObject someObject) {
        try {
            lock.lock();
            concurrentHashMap.put(string, someObject);
        }
        finally {
            lock.unlock();
        }
    public void remove(String string) {
        try {
            lock.lock();
            concurrentHashMap.remove(string);
        }
        finally {
            lock.unlock();
        }
    public SomeObject get(String string) {
        try {
            lock.lock();
            return concurrentHashMap.get(string);
        }
        finally {
            lock.unlock();
        } 
    }

3 个答案:

答案 0 :(得分:1)

  

甚至是必要的锁吗?

不,在您的情况下(添加/删除/获取值)您不需要使用锁。 ConcurrentHashMap专为此类操作而设计。但是,您可能会以下列方式更改方法“添加”:

    public SomeObject add(String string, PARAMETERS_TO_CONSTRUCT_SomeObject) {
        SomeObject result = concurrentHashMap.get(string);        
        if (result == null) {
            result = new SomeObject(PARAMETERS_TO_CONSTRUCT_SomeObject);
            SomeObject old = map.putIfAbsent(string, result);
            if (old != null) {
                result = old;
            }
        }
        return result;
    }

这保证您始终只有一个与给定键关联的SomeObject实例,并防止不必要的实例创建/内存分配。

  

将ConcurrentHashMap设为volatile是否有意义?

我认为在这种情况下安全发布ConcurrentHashMap的最佳方法是将其定义为 final

    private final ConcurrentHashMap<String, SomeObject> concurrentHashMap = new ConcurrentHashMap<>();

答案 1 :(得分:0)

  

甚至是必要的锁吗?因为我的直觉说它不是。

不要急于解雇锁的任何目的。可以在其他地方使用相同的锁,协调更新显示的地图以及与其互斥的一些其他操作。

可能会发现ConcurrentHashMap而不是普通HashMap的使用是消除的过度杀伤力,但即便如此也是如此。

  

将ConcurrentHashMap设为volatile是否有意义?

如果地图变量可能会变异以指向新地图,那么肯定会使volatile成为必需(假设没有锁定来保护该操作)。

答案 2 :(得分:0)

  

甚至是必要的锁吗?

在代码中没有必要提供并发map和lock都是类的私有变量,而lock仅用于同步对map的单个方法调用的访问。在这种特定情况下,使用锁定会删除并发哈希映射在“普通”哈希映射上提供的任何并发优势。

  

在其中一些类中,ConcurrentHashMap是易变的 - 这样做   有什么意义使ConcurrentHashMap变得不稳定?

如果指向并发哈希映射的变量被突变为指向不同的并发哈希映射实例,则变量要么是易失性的,要么是通过使用某些其他机制进行同步来访问。 但总的来说,这似乎是一个非常危险的选择。必须仔细检查成员变量的每次使用,以便在值更改时在线程环境中确定行为是确定性的(并且是正确的)。拿这两个例子:

private volatile ConcurrentHashMap<K, V> map;

public int method1(K key, V value) {
  ConcurrentHashMap<K, V> localMap = map;
  localMap.putIfAbsent(key, value);
  return localMap.size();
}

public int method2(K key, V value) {
  map.putIfAbsent(key, value);
  return map.size();
}

public void mutatorMethod() {
  map = new ConcurrentHashMap<K,V>();
}

这两个方法做广告来做同样的事情:如果一个尚不存在,则为该键设置一个值并返回该地图的大小。 有趣的情况是,如果在putIfAbsent和size调用之间发生了对新映射的赋值,那么当并发调用mutatorMethod时,method2可以返回0。