java - ConcurrentHashMap 上的同步是否正确?

标签 java multithreading synchronization locking concurrenthashmap

我有一个由多个线程访问的键值映射:

private final ConcurrentMap<Key, VersionValue> key_vval_map = new ConcurrentHashMap<Key, VersionValue>();

我的自定义 get()put() 方法遵循典型的 check-then-act 模式。所以需要同步来保证原子性。为了避免锁定整个 ConcurrentHashMap,我定义了:

private final Object[] locks = new Object[10];
{
    for(int i = 0; i < locks.length; i++) 
        locks[i] = new Object();
}

然后 get() 方法开始(它调用 ConcurrentHashMapget() 方法):

public VersionValue get(Key key)
{
    final int hash = key.hashCode() & 0x7FFFFFFF;

    synchronized (locks[hash % locks.length])   // I am not sure whether this synchronization is necessary.
    {
        VersionValue vval = this.key_vval_map.get(key);
        if (vval == null)   
            return VersionValue.RESERVED_VERSIONVALUE;  // RESERVED_VERSIONVALUE is defined elsewhere
        return vval;
    }
}

put() 方法执行(它调用上面的 get() 方法):

public void put(Key key, VersionValue vval)
{
    final int hash = key.hashCode() & 0x7FFFFFFF;

    synchronized (locks[hash % locks.length])   // allowing concurrent writers
    {
        VersionValue current_vval = this.get(key);  // call the get() method above

        if (current_vval.compareTo(vval) < 0)   //  it is an newer VersionValue
            this.key_vval_map.put(key, vval);
    }
}

上面的代码有效。但是,如您所知,在多线程编程中工作远非正确。

我的问题是:

  1. 这种同步机制(尤其是 synchronized (locks[hash % locks.length]))在我的代码中是否必要且正确?

  2. Javadoc on Interface Lock , 它说

Lock implementations provide more extensive locking operations than can be obtained using synchronized methods and statements.

那么在我的代码中用Lock替换synchronization是否可行?

编辑:如果您使用的是 Java-8,请不要犹豫,引用@nosid 的回答。

最佳答案

ConcurrentMap 允许您使用乐观锁定而不是显式同步:

VersionValue current_vval = null;
VersionValue new_vval = null;

do {
    current_vval = key_vval_map.get(key);

    VersionValue effectiveVval = current_vval == null ? VersionValue.RESERVED_VERSIONVALUE : current_vval;

    if (effectiveVval.compareTo(vval) < 0) {
        new_vval = vval;
    } else {
        break;
    }
} while (!replace(key, current_vval, new_vval));

...

private boolean replace(Key key, VersionValue current, VersionValue newValue) {
    if (current == null) {
        return key_vval_map.putIfAbsent(key, newValue) == null;
    } else {
        return key_vval_map.replace(key, current, newValue);
    }
}

它可能在低竞争下有更好的性能。

关于您的问题:

  1. 如果您使用 Guava,请查看 Striped
  2. 不,您在这里不需要Lock的额外功能

关于java - ConcurrentHashMap 上的同步是否正确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24732585/

相关文章:

JavaScript 事件同步

linux - 网络文件系统挂载,本地回退?

java - 如何在 Java 中使用反射实例化内部类?

java - 如何连接两个列表以创建第三个列表?

eclipse 错误 : Unbound classpath container

c# - 如何获取特定 WPF 窗口的当前 SynchronizationContext?

java - 我可以使用多线程一次多次进行相同的休息调用吗?

java - 什么是适合开发的优秀跨平台 Java Web 服务器?

java - ScheduledFuture.cancel() 方法的同步

java - MongoDB 结果集在执行查询后被修改