我有一段代码,在启动时创建一个键到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执行
log.info("hello");
线程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();
}
标题>