有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java内存与同步互斥不一致

更新: 当我第一次发布这篇文章时,我相当肯定代码被破坏了。现在,我不再确定我观察到了什么。我遇到的最大问题是,我似乎无法应用17.4. Memory Model并直接说明它是否应该或不应该工作


以下代码已损坏

这对于它试图实现的目标来说过于复杂,但更进一步,它是线程不安全的,因为我观察到它可以无限期地在c等待。我不担心前者(可以使用ReentrantLockCountDownLatch作为更可靠的代码),但我想知道,后者的原因是什么

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) 个答案

  1. # 1 楼答案

    可能是一个线程在另一个线程开始等待()之前调用notifyAll()。这可能是由于spurious wakeup引起的:

    • 线程1进入临界段
    • 线程2开始等待()
    • 线程2中出现虚假唤醒
    • 线程1进入同步块并通知锁
    • 线程2进入同步块并无限期等待

    或者线程1恰好在线程2之前执行。虽然您的代码在JMM方面是正确的,但它的liveness并不能保证。这就是为什么应该使用倒计时锁存器而不是notify/wait机制