并发编程中的一个经典问题就是生产者与消费者问题。它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。
由于仓库是一个共享的数据结构,我们必须采用同步机制,比如synchronized关键字来控制对它的访问。但是我们有更多的限制因素,如果仓库是满的,生产者不能存放产品,如果仓库是空的,消费者不能取出产品。
对于这些类型的情况,Java在Object对象中提供wait(),notify(),和notifyAll() 方法的实现。这样,我们就可以为任何对象实现同步机制。
- wait():当仓库已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让其他线程执行。
- notify():当生产者/消费者向仓库放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
- notifyAll():同notify(),通知所有等待的线程。
一个线程必须在synchronized代码块中调用wait()方法。如果在synchronized代码块外部调用wait()方法,JVM会抛出IllegalMonitorStateException异常。下面看看示例代码:
/**
* 仓库类
*/
public class Storage {
private int maxSize;
private List<Date> list;
public Storage() {
maxSize = 10;
list = new LinkedList<>();
}
public synchronized void set() {
while (list.size() == maxSize) {
System.out.println("仓库已满,请等待...");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(new Date());
System.out.printf("存放产品: %d\n", list.size());
notify();
}
public synchronized void get() {
while (list.size() == 0) {
System.out.println("仓库为空,暂时没有产品提供,请等待...");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.printf("取出产品:%d: %s\n", list.size(), ((LinkedList<?>) list).poll());
notify();
}
}
/**
* 生产者
*/
public class Producer implements Runnable {
private Storage storage;
public Producer(Storage storage) {
this.storage = storage;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
storage.set();
}
}
}
/**
* 消费者
*/
public class Consumer implements Runnable {
private Storage storage;
public Consumer(Storage storage) {
this.storage = storage;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
storage.get();
}
}
}
测试main()方法:
public static void main(String[] args) {
Storage storage=new Storage();
Producer producer=new Producer(storage);
Thread thread1=new Thread(producer);
Consumer consumer=new Consumer(storage);
Thread thread2=new Thread(consumer);
thread2.start();
thread1.start();
}