package com.producersAndConsumers;
public class Test {
public static void main(String[] args) {
SalesPerson salesPerson = new SalesPerson();
Product product = new Product(salesPerson);
Consumer consumer = new Consumer(salesPerson);
new Thread(product,"生产者A").start();
new Thread(consumer,"消费者B").start();
}
}
// 店员类,produc表示产品的数量,get()方法为生产的方法,sale()方法为消费的方法,两个方法都使用synchronized关键字修饰确保线程安全
class SalesPerson{
private int product = 0 ;
public synchronized void get(){
// 当产品数量达到20时,仓库存满,方法调用wait方法使生产者停止生产
if (product>=20){
System.out.println("已满");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// ++product使产品+1
System.out.println(Thread.currentThread().getName()+" : "+(++product));
// 生产出产品后唤醒消费者线程,
this.notifyAll();
}
public synchronized void sale(){
if (product<=0){
System.out.println("缺货");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"消费了"+(--product));
this.notifyAll();
}
}
//生产者类
class Product implements Runnable{
private SalesPerson salesPerson ;
public Product(SalesPerson salesPerson){
this.salesPerson = salesPerson;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
salesPerson.get();
}
}
}
// 消费者
class Consumer implements Runnable{
private SalesPerson salesPerson ;
public Consumer(SalesPerson salesPerson){
this.salesPerson = salesPerson;
}
@Override
public void run() {
for (int i = 0; i < 20 ; i++) {
salesPerson.sale();
}
}
}
运行结果:
生产者消费者交替进行生产消费,看似很和谐,但是当有多个生产者和消费者同时运行时:
public class Test {
public static void main(String[] args) {
SalesPerson salesPerson = new SalesPerson();
Product product = new Product(salesPerson);
Consumer consumer = new Consumer(salesPerson);
new Thread(product,"生产者A").start();
new Thread(consumer,"消费者B").start();
new Thread(product,"生产者C").start();
new Thread(consumer,"消费者D").start();
}
}
这时运行结果就变得十分离谱,甚至出现了负数,这就是虚假唤醒问题
虚假唤醒问题
假设一开始处于缺货状态,此时两个消费者线程都在等待,如果一个生产者线程抢到资源完成一次生产,就会同时唤醒全部的消费者线程,两个消费者线程同时执行
--product
的操作,导致product的数量变为负数,这种现象称为虚假唤醒问题
对于解决虚假唤醒问题,JDK中给出了说明:对于wait()方法,必须始终在循环中使用:
// 修改后的代码
class SalesPerson{
private int product = 0 ;
public synchronized void get(){
//if (product>=20){
while (product>=20){
System.out.println("已满");
try {
this.wait();
} catch (InterruptedException e) { }
}
System.out.println(Thread.currentThread().getName()+" : "+(++product));
this.notifyAll();
}
public synchronized void sale(){
while (product<=0){
System.out.println("缺货");
try {
this.wait();
} catch (InterruptedException e) {}
}
System.out.println(Thread.currentThread().getName()+" : "+(--product));
this.notifyAll();
}
}