并发编程中的一个经典问题是 生产者-消费者 问题。 有一个数据缓冲区,一个或多个数据生产者将数据保存到缓冲区,一个或多个数据消费者将数据从缓冲区取走。
因为缓冲区是个共享的数据结构,必须使用一种例如 synchronized 关键字的同步机制对它进行控制,只是我们有更多限制:
为此场景,Java 在 Object 类中提供了 wait()、notify() 和 notifyAll() 方法。
本节的示例代码在 com.elanzone.books.noteeg.chpt2.sect04 package中
数据类 (EventStorage)
private int maxSize; private List<Date> storage;
public EventStorage() { maxSize = 10; storage = new LinkedList<Date>(); }
public synchronized void set() { while (storage.size() == maxSize) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } ((LinkedList<Date>)storage).offer(new Date()); System.out.printf("Set: %d\n", storage.size()); notifyAll(); }
public synchronized void get() { while (storage.size() == 0) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.printf("Get: %d: %s\n", storage.size(), ((LinkedList<?>) storage).poll()); notifyAll(); }
生产者线程 : Producer
private EventStorage storage; public Producer(EventStorage storage) { this.storage = storage; }
for (int i = 0; i < 100; i++) { storage.set(); }
消费者线程 : Consumer
private EventStorage storage; public Consumer(EventStorage storage) { this.storage = storage; }
for (int i = 0; i < 100; i++) { storage.get(); }
控制类 : Main
EventStorage storage = new EventStorage();
Producer producer = new Producer(storage); Thread thread1 = new Thread(producer); Consumer consumer = new Consumer(storage); Thread thread2 = new Thread(consumer);
thread2.start(); thread1.start();
本例的要点是 EventStorage 类的 set() 和 get() 方法。
set() 方法检查storage属性中是否有空间。如果已满,则调用 wait() 方法等待空间。 当其他线程调用 notifyAll() 方法,此线程醒来再次检查相关条件。notifyAll() 方法不保证一定会唤醒某线程。 此过程一直重复直到 storage 里有空间,能生成新的事件到 storage。
get()方法的行为也类似。首先它检查storage里是否有事件。如果为空,它将调用 wait() 方法等待事件。 当其他线程调用 notifyAll() 方法,此线程则醒来再次检查相关条件,直到 storage 里有事件为止。
您必须在一个while循环中持续检查相关条件并调用wait()方法。条件不满足时不能继续执行后续操作。
如果您运行本例,您将看到无论生产者和消费者怎么设置、获取事件,storage里不会超过10个事件。