对于下面的代码:
public ReentrantReadWriteLock getLock(String tableName) {
ReentrantReadWriteLock lock = locksMap.get(tableName);
if (lock == null) {
lock = new ReentrantReadWriteLock();
locksMap.put(tableName, lock);
}
}
//其中locksMap是一个HashMap,键为String(tableName),值为ReentrantReadWriteLock(Lock)。
我的问题是,如果线程同时访问这个方法,他们会得到具有相同“tableName”的不同Lock对象,因为Map的get和put方法是分开调用的。
任何带有解释的解决方案将不胜感激? 提前致谢。
最佳答案
使用 ConcurrentMap
通常会产生比 synchronized
block 更好的性能。
Java 5-7:
ConcurrentMap<String, ReadWriteLock> lockMap = new ConcurrentHashMap<>();
ReadWriteLock getLock(String key) {
ReadWriteLock lock = lockMap.get(key);
if (lock == null) {
lock = new ReentrantReadWriteLock();
ReadWriteLock other = lockMap.putIfAbsent(key, lock);
if (other != null) {
// another thread's putIfAbsent won
lock = other;
}
}
return lock;
}
Java 8+:
ConcurrentMap<String, ReadWriteLock> lockMap = new ConcurrentHashMap<>();
ReadWriteLock getLock(String key) {
return lockMap.computeIfAbsent(key, ReentrantReadWriteLock::new);
}
首先,ConcurrentHashMap
之类的实现被记录为不在读取 操作上使用锁。因此,在这种情况下,您打算为单个 key 获取锁的次数似乎比您打算创建新锁的次数要多得多,这将减少线程争用。如果您使用了 synchronized
,即使已经创建了锁,您也会强制每个线程通过临界区处理单个文件。
此外,实现可以执行更高级的锁定形式,甚至是分片 锁定,这样两个编写器就不必互相阻塞(如果写入底层数据结构的不同分区)。同样,synchronized
使用单个监视器对象,无法从了解底层数据结构的细节中获益。
由于 lambda 和函数引用,Java 8 版本变成了单行代码。 ::new
语法引用相邻的 ReentrantReadWriteLock
类的公共(public)、无参数构造函数。 computeIfAbsent
方法只会在必要时调用该构造函数,并且基本上会为您完成上面 Java 7 版本中的所有样板文件。如果创建新对象的成本很高或有不幸的副作用,这将特别有用。请注意,Java 7 版本必须在某些情况下创建新的锁实例,并且新对象可能不会被使用/返回。
关于java - 正确调用 Hashmap 的 get() 和 put() 方法。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27113045/