package com.pccc.pactera.juc01;
public class TestProducterAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Porductor p = new Porductor(clerk);
Consumer c = new Consumer(clerk);
new Thread(p, "生产者A").start();
new Thread(c, "消费者B").start();
new Thread(p, "生产者C").start();
new Thread(c, "消费者D").start();
}
}
/**
* 店员类 有进货,售出 两个操作 等待唤醒机制
*
* @author zhao
*
*/
class Clerk {
private int product = 0;// 产品量初始值
// 进货
public synchronized void get() {
while (product >= 1) {// 为了避免虚假唤醒问题 ,应该总是使用在循环中
System.out.println("产品已满");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
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) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":" + --product);
this.notifyAll();
}
}
/**
* 生产者
*
* @author zhao
*
*/
class Porductor implements Runnable {
private Clerk clerk;
public Porductor(Clerk clerk) {// 构造器
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();// 进货
}
}
}
/**
* 消费者
*
* @author zhao
*
*/
class Consumer implements Runnable {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();// 售出操作
}
}
}
首先走读一下上边代码,声明一个店员类,有进货和出货两个操作,创建消费者类和生产者类实现消费和生产操作,设置循环次数,
注意点1:若使用if...else会出现虚假唤醒问题,查看wait的JDK API可知
对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用:
synchronized (obj) { while (<condition does not hold>) obj.wait(); ... // Perform action appropriate to condition }此方法只应由作为此对象监视器的所有者的线程来调用。有关线程能够成为监视器所有者的方法的描述,请参阅
notify
方法。
所以此处使用while ;
注意点2:在进货和售出两个方法中,执行++product和唤醒所有线程,考虑生产者和消费者线程可能会出现不同步,因为网络或应用的延迟问题,会出现当消费者循环次数为2时,生产者循环次数为1,导致主方法运行时,有线程处于等待状态,无法结束运行。