生产者和消费者是一个多线程同步的经典案例,该问题描述了两个共享固定大小缓冲区的线程,即所谓的“生产者”和“消费者”,顾名思义,生产者指的就是生产一定的数据量到缓冲区,而消费者就是从缓冲区取走一定的数据。
生产者和消费者问题要解决一个死锁问题,就是当缓冲区已经满的时候,生产者占着它等待消费者来取走数据,而消费者则等着生产者让出缓冲区的权利好取走数据,于是就相互等待,从而造成死锁。
本程序只有一个生产者和一个消费者,使用wait和notify(nitify)方法来避免死锁问题,这里得提一下wait和notify(notifyAll)方法,线程中的wait、notify以及notifyAll方法:都是定义在Object类中的final方法,即所有的类都默认拥有这3个方法,但只用于synchronized关键字作用的范围内,并且是搭配着一起使用;wait方法是通知当前线程等待并释放对象锁;notify方法是通知等待此对象锁的一个线程重新获得线程锁;notifyAll是唤醒所有等待此对象锁的线程。
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack myStack = new SyncStack(6);
Producer producer = new Producer(myStack);
Consumer consumer = new Consumer(myStack);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
t1.start();
t2.start();
}
}
class Bread {
private int id;
Bread(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
class SyncStack {
private int size;
private int index = 0;
private Bread[] myBread;
SyncStack(int size) {
this.size = size;
myBread = new Bread[size];
}
public synchronized void put(Bread bread) {
while (index == size) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
myBread[index] = bread;
++index;
System.out.println("Producer puts bread: " + bread.getId());
this.notify();
}
public synchronized Bread remove() {
while (index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
--index;
System.out.println("Consumer removers bread: " + myBread[index].getId());
this.notify();
return myBread[index];
}
}
class Producer extends Thread {
private SyncStack myStack;
Producer(SyncStack myStack) {
this.myStack = myStack;
}
public void run() {
for (int i = 1; i <= 20; ++i) {
myStack.put(new Bread(i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class Consumer extends Thread {
private SyncStack myStack;
Consumer(SyncStack myStack) {
this.myStack = myStack;
}
public void run() {
for (int i = 1; i <= 20; ++i) {
Bread bread = myStack.remove();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}