Java Notify似乎正在唤醒多个线程

Java Notify似乎正在唤醒多个线程,java,multithreading,wait,notify,Java,Multithreading,Wait,Notify,我正在使用wait()和notify()执行一个示例程序,但是当调用notify()时,会唤醒多个线程而不是一个线程 代码是: public class MyQueue<T> { Object[] entryArr; private volatile int addIndex; private volatile int pending = -1; private final Object lock = new Object(); priv

我正在使用
wait()
notify()
执行一个示例程序,但是当调用
notify()
时,会唤醒多个线程而不是一个线程

代码是:

public class MyQueue<T> {

    Object[] entryArr;
    private volatile int addIndex;

    private volatile int pending = -1;
    private final Object lock = new Object();

    private volatile long notifiedThreadId;
    private int capacity;

    public MyQueue(int capacity) {
        entryArr = new Object[capacity];
        this.capacity = capacity;
    }

    public void add(T t) {
        synchronized (lock) {
            if (pending >= 0) {
                try {
                    pending++;
                    lock.wait();
                    System.out.println(notifiedThreadId + ":" + Thread.currentThread().getId());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else if (pending == -1) {
                pending++;
            }
        }

        if (addIndex == capacity) { // its ok to replace existing value
            addIndex = 0;
        }

        try {
            entryArr[addIndex] = t;
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("ARRAYException:" + Thread.currentThread().getId() + ":" + pending + ":" + addIndex);
            e.printStackTrace();
        }

        addIndex++;

        synchronized (lock) {
            if (pending > 0) {
                pending--;
                notifiedThreadId = Thread.currentThread().getId();
                lock.notify();
            } else if (pending == 0) {
                pending--;
            }
        }
    }

}

public class TestMyQueue {

    public static void main(String args[]) {
        final MyQueue<String> queue = new MyQueue<>(2);

        for (int i = 0; i < 200; i++) {
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < Integer.MAX_VALUE; i++) {
                        queue.add(Thread.currentThread().getName() + ":" + i);
                    }
                }
            };
            Thread t = new Thread(r);
            t.start();
        }
    }

}
这里我看到115个线程通知了两个线程,84个线程通知了两个线程;因此,我们看到了
ArrayIndexOutOfBoundsException

115:84

115:111

84:203

84:200

ARRAYException:200:199:3

ARRAYException:203:199:3
节目中的问题是什么

节目中的问题是什么

您的代码中有几个问题可能导致这种行为。首先,正如@Holder所评论的,有很多代码段可以由多个线程同时运行,应该使用
synchronized
块来保护它们

例如:

if (addIndex == capacity) {
    addIndex = 0;
}
如果多个线程运行此操作,则多个线程可能会看到
addIndex==capacity
,并且多个线程将覆盖第0个索引。另一个例子是:

addIndex++;
如果两个线程试图同时执行此语句,则这是一个典型的争用条件。如果
addIndex
之前为0,则在两个线程执行此语句后,
addIndex
的值可能为1或2,具体取决于竞争条件

任何可以由多个线程同时执行的语句都必须在
synchronized
块中正确锁定或以其他方式保护。即使您有
volatile
字段,仍然可能存在竞争条件,因为有多个操作正在执行


另外,一个典型的错误是在检查数组上的溢出或不足流时使用
if
语句。它们应该是
while
语句,以确保您没有类消费者-生产者竞争条件。查看或查看相关的SO问题:

您似乎错过了
synchronized
的实际用途。它是为了保护对共享资源的访问,而不是在访问完全不受保护的共享资源时执行等待和通知。此外,您应该认真阅读的文档,尤其是“…虚假唤醒是可能的,并且此方法应始终在循环中使用”部分。感谢您的快速回复。我知道我们可以使用并发锁。但我的任务是使用wait()和notify()创建锁。因此,在任何时候,只有一个线程应该在同步块之间执行代码?
synchronized
块必须跨越整个操作,包括对共享数据结构的每次访问,而不仅仅是执行
wait
notify
的部分。您正在访问
entryArr
addIndex
块之外的
synchronized
块,并将
addIndex
声明为
volatile
没有帮助,因为它不会使更新原子化。
addIndex++;