0

我正在从互联网上阅读一些东西。这是关于下面代码的问题。以下从某个队列实现中检索整数值的代码是否正确?答案是这样的:

尽管上面的代码使用队列作为对象监视器,但它在多线程环境中的行为并不正确。这样做的原因是它有两个独立的同步块。当两个线程在第 6 行被另一个调用 notifyAll() 的线程唤醒时,两个线程一个接一个地进入第二个同步块。在第二个块中,队列现在只有一个新值,因此第二个线程将轮询一个空队列并获取 null 作为返回值。

我在想同步块会阻止不同的线程同时访问这个资源,不是吗?谢谢你。

public Integer getNextInt() {

    Integer retVal = null;

    synchronized (queue) {
        try {
            while (queue.isEmpty()) {
                queue.wait();
           }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    synchronized (queue) {
        retVal = queue.poll();
        if (retVal == null) {
            System.err.println("retVal is null");
            throw new IllegalStateException();
        }
    }

    return retVal;
}
4

2 回答 2

2

问题比您的问题中描述的要广泛得多。它甚至不需要两个通知线程(或一个notifyAll)来陷入麻烦。问题是线程调度是不确定的。

考虑以下场景:

  • 线程A进入第一个synchronizedblock,找到一个空队列等待
  • 另一个线程将一个项目放入队列并通知一个线程。
  • 线程 A 唤醒并离开第一个synchronized
  • 线程 B 进入第一个synchronized块并找到一个非空队列并且不等待!
  • 线程 B 离开第一个synchronized

所以现在,两个线程,AB,即将进入第二个synchronized块,顺序无关紧要,因为此时很明显只有一个线程可以消费一个项目,另一个会失败(除非另一个线程碰巧放了一些东西进入中间的队列)。

实际上,它甚至不需要任何通知,例如,如果两个线程在队列恰好包含一个元素时调用此方法。他们都可以在尝试执行第二个块之前执行第一个块,发现队列非空。


底线是,如果你有某种条件,执行条件的代码wait(意味着检查条件)和依赖条件的代码必须在一个synchronized块中,或者在其他类型的锁的情况下,必须在整个代码中保持锁定。

于 2015-01-19T16:57:17.283 回答
0

啊,我现在明白了。所以如果同时通知2个线程,一个进入块并轮询队列,第二个因为同步块而等待。然后当第二个线程进入时,队列已经空了......非常感谢。这是我在网上找到的一个例子。

于 2015-01-19T16:25:49.447 回答