更新:
当我第一次发布这个时,我相当确定代码被破坏了。现在,我不再确定我观察到了什么。我遇到的最大问题是我似乎无法应用 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 :
但是,这似乎并不能证明或反驳任何事情。
编辑:(上述问题未能真正解释这段代码应该做什么。)
预期的:
brokenFoo()
被多个线程调用,上面的代码应该提供对 // critical section
的互斥。 brokenFoo()
,则只有一个应该继续进入 // critical section
,而其他线程则在之前的某个地方等待。 // critical section
中的线程退出后,另一个应该继续代替它。 实际的:
c
中没有其他线程,仍有线程在 brokenFoo()
处等待。 最佳答案
可能是一个线程在 另一个线程开始 wait() 之前调用 notifyAll() 的情况。这可能是由于 spurious wakeup 导致的:
或者线程 1 恰好在线程 2 之前执行。虽然您的代码在 JMM 方面是正确的,但不能保证其 liveness 。这就是为什么您应该使用 CountDownLatch 而不是通知/等待机制的原因。
关于java - 与同步互斥锁的内存不一致,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24175558/