java内存与同步互斥不一致
更新: 当我第一次发布这篇文章时,我相当肯定代码被破坏了。现在,我不再确定我观察到了什么。我遇到的最大问题是,我似乎无法应用17.4. Memory Model并直接说明它是否应该或不应该工作
以下代码已损坏
这对于它试图实现的目标来说过于复杂,但更进一步,它是线程不安全的,因为我观察到它可以无限期地在c
等待。我不担心前者(可以使用ReentrantLock
或CountDownLatch
作为更可靠的代码),但我想知道,后者的原因是什么
static final ConcurrentHashMap<Integer, Object> mutex = new ConcurrentHashMap<>();
public static brokenFoo() {
Object ourLock = new Object();
for (;;) {
Object theirLock = mutex.putIfAbsent(0, ourLock);
if (theirLock == null) {
break;
}
synchronized (theirLock) { // a
if (mutex.get(0) != theirLock) { // b
continue;
}
theirLock.wait(); // c
} // d
}
try {
// critical section
} finally {
synchronized (ourLock) { // e
mutex.remove(0); // f
ourLock.notifyAll(); // g
} // h
}
}
我从happens-befores的角度思考:
- hb(f,h)和hb(h,a)因此hb(f,a)
- hb(c,d)和hb(d,e)因此hb(c,e)
但是,这似乎并不能证明或反驳任何事情
编辑:(上述问题无法真正解释此代码应该做什么。)
预期:
brokenFoo()
被多个线程调用,上面的代码应该在// critical section
上提供互斥李>- 如果两个或多个线程同时进入
brokenFoo()
,则只有一个线程应继续执行// critical section
,而其他线程则在前面等待李> - 在
// critical section
中的线程退出后,另一个线程应该继续替换它李>
实际值:
- 已经观察到有线程在
c
等待,即使在brokenFoo()
中没有其他线程李>
# 1 楼答案
可能是一个线程在另一个线程开始等待()之前调用notifyAll()。这可能是由于spurious wakeup引起的:
或者线程1恰好在线程2之前执行。虽然您的代码在JMM方面是正确的,但它的liveness并不能保证。这就是为什么应该使用倒计时锁存器而不是notify/wait机制