Java多线程编程核心技术——生产者消费者模型

生产者消费者模型是并发中的经典问题,具体场景是有一块缓冲区作为仓库,生产者可以向其添加产品,消费者可以从中取出产品。解决生产者消费者问题可以采用两种方式:wait()/notify方式和BlockingQueue方式,在此主要讨论第一种,关于第二种方法可以参考Ranger的Audit模型。

wait()/notify()是Object的两个方法,也是并发中常见的两个方法:wait会使线程阻塞,直到一个notify通知其继续执行。下面通过一段代码来说明这两个方法。

public class MyThread implements Runnable {
  private String name;
  private List list = new ArrayList();
  private final int size = 10;

  public void produce(int num) throws Exception {
    while (true) {
      synchronized (list) {
        while (list.size() + num > size) {
          System.out.println(Thread.currentThread().getName()+"生产过剩,等待消费");
          list.wait();
        }
        System.out.println(Thread.currentThread().getName()+"正在生产");
        for (int i = 0; i < num; i++) {
          list.add("hello, world");
        }
        list.notify();
      }
      Thread.sleep(1000);
    }
  }
  public void consume() throws Exception {
    while (true) {
      synchronized (list) {
        while (list.size() == 0) {
          System.out.println(Thread.currentThread().getName()+"已无产品可消费,等待生产");
          list.wait();
        }
        System.out.println(Thread.currentThread().getName()+"正在消费");
        list.remove(0);
        list.notify();
      }
      Thread.sleep(1000);
    }
  }
  public void setName(String name) {
    this.name = name;
  }

  public void run() {
    try {
      if ("producer".equals(name)) {
        produce(1);
      }
      else {
        consume();
      }
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) {
    try {
      MyThread myThread = new MyThread();
      myThread.setName("producer");
      Thread t1 = new Thread(myThread);
      Thread t4 = new Thread(myThread);
      t1.start();
      t4.start();
      Thread.sleep(1);
      myThread.setName("consumer");
      Thread t2 = new Thread(myThread);
      Thread t3 = new Thread(myThread);
      t2.start();
      t3.start();
    }
    catch (Exception e) {

    }
  }
}

MyThread提供了list作为产品仓库,produce和consume方法模拟生产产品和消费产品的过程。值得注意的是,因为list涉及多个线程对其操作,不管是生产还是消费,都需要对list加锁,防止出现“脏读”。当list中产品过剩时,需要暂停生产者的produce过程,直到消费者消费掉一定的产品,使仓库能有足够的空间容纳新产品并向生产者发送通知生产者才可以继续生产;同样,当仓库空了,需要暂停消费者的consume过程,直到生产者生产了产品之后向消费者发送通知,消费者才可以继续消费。此处的暂停/通知就是通过wait()/notify()方法实现的。

wait()与notify()都必须在同步块中使用,wait()使线程等待,notify()则会通知线程继续运行,需要注意的是,如果有多个处在wait状态的线程,一个notify会随机的通知其中一个线程继续运行,其它线程仍旧处于wait状态。而notifyAll()方法会通知所有处于wait状态的线程继续运行,但是因为wait处于同步代码块中,接到通知只是会让线程退出wait状态,至于能否真正的继续运行还要看线程能否抢到同步锁。


你可能感兴趣的:(读书笔记)