【问题标题】:Memory inconsistency with synchronized mutex内存与同步互斥锁不一致
【发布时间】:2014-06-12 02:26:42
【问题描述】:

更新: 当我第一次发布这个时,我相当确定代码被破坏了。现在,我不再确定我观察到了什么。我遇到的最大问题是我似乎无法申请 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 中的线程退出后,应继续使用另一个线程来代替它。

实际:

  • 据观察,尽管brokenFoo() 中没有其他线程,但仍有线程在 c 处等待。

【问题讨论】:

  • 你能解释一下你想用这段代码实现什么吗?
  • 通过上面的代码,我想以最简单的方式说明上述问题,以便我可以输入关于互斥和内存一致性方面的错误.这样我可以更好地理解 synchronized 的工作原理和不工作原理,以及 Java 内存模型的规定,以便我可以在未来的案例中应用这些常识。
  • 是的,但您的期望是什么?这个方法是多线程调用的?它应该怎么做?它实际上是做什么的?它只是在cwait() 上阻塞?
  • 对不起,我明白你的意思了。我已经修改了问题。
  • @Voo: 重新排序 removenotify 不应该有任何显着影响,因为这些操作在 synchronized 块内,因此 atomic 用于同步的其他线程同一个对象。

标签: java multithreading synchronization


【解决方案1】:

可能是一个线程调用 notifyAll()另一个线程开始 wait()。这可能是由于spurious wakeup

  • 线程 1 进入临界区
  • 线程 2 开始等待()
  • 线程 2 中发生虚假唤醒
  • 线程1进入同步块并通知锁
  • 线程2进入同步块,无限等待

或者线程 1 恰好在线程 2 之前执行。虽然您的代码在 JMM 方面是正确的,但不能保证其 liveness。这就是为什么您应该使用 CountDownLatch 而不是通知/等待机制。

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-10
  • 2021-06-04
  • 2014-05-02
  • 2012-08-28
  • 1970-01-01
  • 2010-09-16
相关资源
最近更新 更多