HashMap和ReentrantLock的并发性问题



我有一段代码,在启动时创建一个键到ReentrantLock的HashMap

void constructor() {
this.lockMap = new HashMap<>();
for (int i=0; i<100; i++) {
this.lockMap.put(i, new ReentrantLock(true));
}
}

在并发执行期间,我尝试用以下方式锁定lockMap中的锁:

runConcurrently() {
ii = 10;
if (!lockMap.containsKey(ii)) {
log.error("lock id is not found in the lockMap " + ii);
}

locked = lockMap.get(ii).tryLock();
if (!locked) {
return;
}
runCriticialSection();
lockMap.get(ii).unlock();
}
void runCriticialSection() {
log.info("hello");
log.info("I'm here");                                                      
}

所以这是我所看到的每4个小时运行一次的代码,在非常罕见的情况下。

我看到这些日志:

你好。
你好。
我在这里。
我在这里。

,然后在第三次访问相同键的hasmap后,我看到了这个日志ii =10:

锁id在映射中找不到
NullPointerExeception……试着打开地图。

我应该在保证排序中看到:

你好。
我在这里。
你好。
我在这里。

Hashmap在执行期间永远不会被修改。

是否存在hashmap不是并发hashmap的问题?在没有修改的情况下,get不是线程安全的吗?由于并发hasmap中的锁慢,我特别不使用它。但是hashmap只在启动时创建,之后永远不会修改。我发现它非常奇怪,似乎锁已获得两次,似乎元素从地图中丢失。

如果在构造函数之后不修改映射,则映射本身没有并发性问题。如果是这样,线程将只能看到映射的最终版本。否则,行为未定义。

临界区无独占访问

从您的输出中可以看出(至少)有两个线程同时访问了runCriticialSection

这是因为您对ii的每个值使用了不同的锁。只有当其他线程使用同一锁时,锁才会排除其他线程对它的锁定!因此,不使用相同ii值的线程将毫不费力地同时运行runCriticialSection。可能导致描述输出异常如上所示,如下:

  1. 线程1执行log.info("hello");
  2. 线程2执行log.info("hello");线程1执行log.info("I'm here");线程2执行log.info("I'm here");

如果你想独占访问一个section,总是对该section使用相同的锁。

<标题>

编码问题当ii映射到锁的检查失败时,您不应该继续,而应该返回或抛出异常。如果没有,locked = lockMap.get(ii).tryLock();抛出NullPointerExcetpion,因为lockMap.get(ii)返回null.

在锁锁和解锁之间,运行的是用户代码,格式为runCriticalSection。如果您稍后更改该方法的实现,它会开始抛出问题:您的锁永远不会解锁!总是使用try…最后

修复这些问题,可能会导致以下代码:

if (!lockMap.containsKey(ii)) {
log.error("lock id is not found in the lockMap " + ii);
return;
}
locked = lockMap.get(ii).tryLock();
if (!locked) {
return;
}
try {
runCriticialSection();
}
finally {
lockMap.get(ii).unlock();
}

实际上,我会把锁放在一个局部变量中,但这是一个个人意见的问题。

ReentrantLock lock = lockMap.get(ii);
if (lock == null) {
log.error("lock id is not found in the lockMap " + ii);
return;
}
locked = lock.tryLock();
if (!locked) {
return;
}
try {
runCriticialSection();
}
finally {
lock.unlock();
}

最新更新