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

时间:2016-07-21 08:28:04

标签: java multithreading synchronized producer-consumer

我在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 synchronized produce()中的代码包裹在consume()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(); } }

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();


    }
}

这是什么问题?这是线程饥饿还是死锁的情况?

编辑:致电课程:

$.ajax({
    type: 'GET',
    url: '/first_line_benefits',
    data: { tablename: tablename },
    success: function(data) {
        var dataArray = data[0];
        var ret = "";
        for (var j = 1; j < dataArray.length; j++) {
            ret += "<div class=\"filter_button col-md-4\">";
            ret += "<select class=\"selectpicker\" multiple><option value='0' selected>Nothing selected</option>";
                for (var i = 0; i < dataArray[j].length; i++)
                {
                    ret += "<option>"+ dataArray[j][i] + "</option>";
                }
                ret += "</select></div>";
            }
            $('#filter').html(ret);
        }
    });

2 个答案:

答案 0 :(得分:6)

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

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

答案 1 :(得分:1)

使用synchronized外部循环创建read barrier。因此,生产者/消费者将看到您正在检查list的最新list.size()内部循环。这就是为什么它会在while块内移动synchronized循环后才能生效。

在您的情况下,我还建议您在生产者/消费者中使用单个同步块。

例如,在您的实现中,如果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();
    }
  }
}