package fighting;
public class ThreadProducerConsumer {
/**
* 生产者-消费者的例子
* 对于多个生产者和消费者,为什么要定义while判断标记,
* 原因:让被唤醒的线程再一次判断标记。
*
* 总结:当出现多个生产者和消费者的情况,必须使用while判断标记,用notifyAll唤醒线程。
*
* 为什么使用notifyAll,因为需要唤醒对方线程,
* 只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。
*/
public static void main(String[] args) {
Resource1 res=new Resource1();
new Thread(new Producer(res)).start();
new Thread(new Producer(res)).start();
new Thread(new Consumer(res)).start();
new Thread(new Consumer(res)).start();//如果是多个线程消费,就有可能发生,生产者生产一个,不同消费者消费同一个产品的情况;
//或者生产者生产两次,消费者只消费了一次。
}
}
class Resource1{
private String name;
private int count = 1;
private boolean flag=false;
/**
* 在这里分析一下:t1、t2是生产者线程,t3、t4是消费者线程。
* 假设t1先获取到cpu执行权,
* 一,t1进入set方法,判断if(flag)为false,t1生产一个商品,把flag置为true,t1接着又循环进入判断if(flag)为true,t1进入等待状态,放弃执行资格。
* 二,这时,假设t2获得cpu执行权,t2进入set方法,判断if(flag)为true,进入等待状态,放弃执行资格。
* 三,t3进入out方法,判断flag为true,消费一个商品,把flag置为false,notify唤醒t1,
* 但是t3才有执行权,t3判断if(flag)为false,t3进入等待,放弃资格。
* 四,t4进入out方法,进入等待,放弃资格。
* 五,此时t1是唤醒状态,生产一个商品,flag置为true,notify唤醒t2,t1仍有执行权,t1判断if(flag)为true,进入等待状态,放弃资格。
* 六,此时,t2处于唤醒状态,t2继续往下执行,而不会去判断if(flag)了,直接又生产一个商品,这就导致生产了两个商品。t2把flag置为true,notify唤醒t3,
* t3这时只消费了t2生产的商品,t1生产的商品就没有消费了。
* 原因就是在线程唤醒后,没有再去判断flag,把if改为while就会实现唤醒后还会判断flag了。
* 但是这样,会发生几个线程都会等待。
* 这时就可以使用notifyAll方法解决问题。
*
*/
public synchronized void set(String name){
// if(flag){
while(flag){//用while判断后,用this.notify唤醒本方,而不能唤醒对方,所以会导致都处于等待状态。
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name=name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者"+this.name);
flag=true;
// this.notify();
this.notifyAll();//notifyAll唤醒在此对象监视器上等待的所有线程
}
public synchronized void out(){
// if(!flag){
while(!flag){
try {
wait();//这里this省略了
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"...消费者"+this.name);
flag=false;
// this.notify();
this.notifyAll();
}
}
class Producer implements Runnable{
private Resource1 res;
Producer(Resource1 res){
this.res=res;
}
public void run(){
while(true){
res.set("商品");
}
}
}
class Consumer implements Runnable{
private Resource1 res;
Consumer(Resource1 res){
this.res=res;
}
public void run(){
while(true){
res.out();
}
}
}