在 Java 中使用 wait() 和 notify() 被阻止

Blocked using wait() and notify() in Java

我在 Java 中使用 wait()notify() 编写生产者和消费者代码。 Thread-0 创建并在 produce() 上调用,Thread-1 创建并在 consume().

上调用
public class Processor {

  private volatile List<Integer> list = new ArrayList<>();
  private final int MAX_CAPACITY = 5;
  Object lock = new Object();

  public void produce() throws InterruptedException {

    while (true) {

      while (list.size() == MAX_CAPACITY) {
        System.out.println("List is full! Producer is Waiting....");
        synchronized (lock) {
          lock.wait();
        }
      }

      synchronized (lock) {
        int random = new Random().nextInt(100);
        list.add(random);
        System.out.println("Added to list:" + random);
        lock.notify();
      }
    }
  }

  public void consume() throws InterruptedException {

    while (true) {

      while (list.size() == 0) {
        System.out.println("List is empty!! Consumer is Waiting...");
        synchronized (lock) {
          lock.wait();
        }
      }

      synchronized (lock) {
        int i = list.remove(0);
        System.out.println("Removed from list:" + i);
        lock.notify();
      }
    }
  }
}

问题是在执行过程中,程序在 produce():

之后停止
List is empty!! Consumer is Waiting...
Added to list:22
Added to list:45
Added to list:72
Added to list:91
Added to list:51
List is full! Producer is Waiting....

我不明白这里有什么问题。我不知何故发现将 while 循环中的代码包装在 produce()consume() 中的 synchronized 块中可以解决问题。

produce()

synchronized (lock) {
                while (list.size() == MAX_CAPACITY) {
                    System.out.println("List is full! Producer is Waiting....");

                    lock.wait();
                }

consume

synchronized (lock) {
                while (list.size() == 0) {
                    System.out.println("List is empty!! Consumer is Waiting...");

                    lock.wait();
                }
            }

这里有什么问题?是线程饥饿还是死锁?

编辑: 调用 class:

public class App {
    public static void main(String[] args) {
        final Processor processor = new Processor();

        Runnable r1 = new Runnable() {

            @Override
            public void run() {
                try {
                    processor.produce();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        };

        Runnable r2 = new Runnable() {

            @Override
            public void run() {
                try {
                    processor.consume();
                } catch (InterruptedException e) {

                    e.printStackTrace();
                }
            }
        };

        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);

        t1.start();
        t2.start();


    }
}

当您执行 list.size() 时,它不是线程安全的,并且不能保证您会在另一个线程中看到该值发生更改。如果 JIT 检测到您没有在该线程中更改它,它甚至可以内联该值。

通过将 synchronized 块放在循环之外,您可以确保值的变化是可见的(因为它也在 while(true) 循环内。

使用 synchronized 外部循环创建 read barrier。因此 producer/consumer 将在您正在检查 list.size() 的循环中看到最新的 list。这就是为什么在 synchronized 块内移动 while 循环后它起作用的原因。

对于您的情况,我还建议您在 producer/consumer 中使用单个同步块。

例如,在您的实现中,如果 list.size() == 0 变为 false 用于消费者,它将释放对 lock 对象的锁定,然后在下一条语句中尝试再次重新获取锁定以进行消费数据,这是不必要且低效的。它应该是这样的:

public void consume() throws InterruptedException {

  while (true) {
    synchronized (lock) {
      while (list.size() == 0) {
        System.out.println("List is empty!! Consumer is Waiting...");

        lock.wait();
      }

      int i = list.remove(0);
      System.out.println("Removed from list:" + i);
      lock.notify();
    }
  }
}