多生产者与消费者仓库剩余问题
来源:7-6 用wait/notify实现

weixin_慕UI5143857
2021-04-12
悟空老师. 您好:
在程序有多个消费者与生产者情况下, storage会出现无法被完全消费完的情况, 代码如下:
public class ProducerConsumerModelWaitNotify {
public static void main(String[] args) throws InterruptedException {
EventStorage storage = new EventStorage(10);
Producer producer = new Producer(storage);
Consumer consumer = new Consumer(storage);
new Thread(producer).start();
new Thread(producer).start();
new Thread(consumer).start();
new Thread(consumer).start();
Thread.sleep(1000);
System.out.println(storage.storage.size());
}
}
/**
* 生产者
*/
class Producer implements Runnable {
private final EventStorage storage;
public Producer(EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
for(int i=0; i<3; i++) {
storage.put(new Date());
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable {
private final EventStorage storage;
public Consumer(EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
for(int i=0; i<3; i++) {
storage.take();
}
}
}
/**
* Storage队列
*/
class EventStorage {
public int maxSize;
public LinkedList<Date> storage;
public EventStorage(int maxSize) {
this.maxSize = maxSize;
this.storage = new LinkedList<>();
}
public synchronized void put(Date date) {
// 判断仓库是否满了, 满了进入watting
if(this.storage.size() == maxSize) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
this.storage.add(date);
System.out.println("仓库里有了: " + this.storage.size() + "个产品");
// 唤醒与这个对象锁有关的其中一个生产者
notify();
}
public synchronized void take() {
// 仓库是否是空了. 空了进入watting
if(this.storage.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
// 取出产品, 并唤与此锁有关的处于watting状态的生产者生产
System.out.println("消费者拿到了" + storage.poll() + "仓库还剩下" + storage.size());
notify();
}
}
两个生产者, 两个消费者, 每个生产者生产3次, 每个消费者消费3次.
程序都能正常结束, 正常停止.
但是:
在多次运行之后, 发现了如下运行结果:
仓库里有了: 1个产品
仓库里有了: 2个产品
仓库里有了: 3个产品
消费者拿到了Mon Apr 12 23:26:37 CST 2021仓库还剩下2
消费者拿到了Mon Apr 12 23:26:37 CST 2021仓库还剩下1
消费者拿到了Mon Apr 12 23:26:37 CST 2021仓库还剩下0
消费者拿到了null仓库还剩下0
仓库里有了: 1个产品
仓库里有了: 2个产品
仓库里有了: 3个产品
消费者拿到了Mon Apr 12 23:26:37 CST 2021仓库还剩下2
消费者拿到了Mon Apr 12 23:26:37 CST 2021仓库还剩下1
1
有一个消费者在仓库空的情况下取到了一个空值, 导致程序执行完毕, stroage还存在一个产品
按道理, storage种take() 与 put() 都是被synchronized修饰的, 并且持有的moniter都是storage这个对象锁.所以无论多少生产者多少消费者在操作storage应该仅一个线程能够去访问storage, 怎么会有线程能够取到null?
最后经过尝试, 如果在消费者与生产者再加一把锁(代码如下), 可以解决仓库剩余问题, 但是个人无法解释原因:
/**
* 生产者
*/
class Producer implements Runnable {
private final EventStorage storage;
public Producer(EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
synchronized (this) {
for(int i=0; i<3; i++) {
storage.put(new Date());
}
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable {
private final EventStorage storage;
public Consumer(EventStorage storage) {
this.storage = storage;
}
@Override
public void run() {
synchronized (this) {
for(int i=0; i<3; i++) {
storage.take();
}
}
}
}
恳请老师指点一二.
写回答
1回答
-
weixin_慕UI5143857
提问者
2021-04-13
找到问题了,notify唤醒的可能是另一个消费者
00
相似问题