问题:
有一个生产者和一个消费者,和一个仓库。仓库容量为4,生产10次和消费10次
问题描述
程序多次运行出现不能正常退出现象:
问题代码:
//生产者
class Product extends Thread{
private Store store;
public Product(Store store) {
this.store = store;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
store.produce();
}
}
}
//消费者
class Consumer extends Thread{
private Store store;
public Consumer(Store store) {
this.store = store;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
store.consume();
}
}
}
//仓库
class Store{
private int memory = 0;
//生产方法
public synchronized void produce(){
if(memory < 4){
memory ++;
System.out.println("生产第" + memory + "产品");
notify();
}else{
System.out.println("仓库产品已满,放不下,等待消费");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费方法
public synchronized void consume(){
if(memory > 0){
System.out.println("消费第" + memory + "产品");
memory--;
notify();
}else {
System.out.println("仓库没有产品可以消费,等待生产");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//测试代码:
public static void main(String[] args) {
Store store = new Store();
Product product = new Product(store);
Consumer consumer = new Consumer(store);
product.setName("生产者");
consumer.setName("消费者");
product.start();
consumer.start();
}
原因分析:
在生产者和消费者中是循环生产和消费的,当进入仓库开始判断是否进行生产或消费时,如果此时仓库是满的或者空的,那么不进行生产或消费。但是在生产者和消费者的循环中是循环了一次的,从而导致生产者或消费者一方不能完整的进行10次生产或消费,在循环10次之后线程结束。而另一个线程没有循环10次,还在运行,如果仓库满了或仓库空了,则要wait(),但是此时另一个线程已经结束,无法进行生产和消费,所以会出现程序不能正常结束的现象。
解决方案:
方法1 :
可以将生产者和消费者中的循环改成while,仓库中的生产方法和消费方法根据是否进行生产返回一个boolean值,在生产者和消费者的循环中判断返回的boolean值,进而选择是否是一次有效的生产或者循环。真实的循环次数可能大于10,而真正进行生产和消费的次数为10次。代码如下:
//生产方法
public synchronized boolean produce(){
if(memory < 4){
memory ++;
System.out.println("生产第" + memory + "产品");
notify();
return true;
}else{
System.out.println("仓库产品已满,放不下,等待消费");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
}
//消费方法中修改方式相同
//生产者中的run方法
@Override
public void run() {
while(count < 10) {
if(store.produce()){
count++;
}
}
}
//消费者中的run方法修改方式相同
方法2 :
wait()后的线程,再次被唤醒时会接着上次运行到的地方继续向下执行,所以可以在生产方法和消费方法中的判断语句由if改为while。这也是源码中写的示例:
修改后的代码如下:
//生产方法
public synchronized void produce(){
while(memory >= 4){
System.out.println("仓库产品已满,放不下,等待消费");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
memory ++;
System.out.println("生产第" + memory + "产品");
notify();
}
//消费方法中修改方式相同