用多线程实现生产者消费者模式(一个生产者,一个消费者),在一个生产者生产了一个面包之后,消费者消费
一个面包,面包不能一直生产,也不能一直消费,也不能先消费,后生产。代码如下:
package bean;
/*
* 1.两个线程同时操作同一份数据,如果不加上同步,那么会出现同步安全问题,比如
* 可能先消费,在生产。
* 2.在方法上加上了同步之后,有效的阻止了先消费,在生产的情况。但是,会出现一
* 直生产,或者一直消费同一个面包的情况。
* 3.要想阻止一直生产,就要使用等待唤醒机制,定义一个标记,在生产后改标记,使判断成功后让线程等待。
* */
public class ALotThreat {
public static void main(String[] args) {
Resource resource=new Resource();
ShengChanZhe sc=new ShengChanZhe(resource);
XiaoFeiZhe xf=new XiaoFeiZhe(resource);
Thread scz=new Thread(sc);
Thread xfz=new Thread(xf);
scz.start();
xfz.start();
}
}
class ShengChanZhe implements Runnable{
//用构造函数的方式使多个线程共用同一份资源
private Resource rs;
public ShengChanZhe(Resource rs) {
this.rs=rs;
}
@Override
public void run() {
//运行生产者的方法
while(true) {
rs.setName("面包");
}
}
}
class XiaoFeiZhe implements Runnable{
private Resource rs;
public XiaoFeiZhe(Resource rs) {
this.rs=rs;
}
@Override
public void run() {
while(true) {
rs.getName();
}
}
}
class Resource{
private String name;
private int count=0;
//通过标记来阻止一直生产面包的情况
private boolean flag = false;
//将操作同一数据的方法进行同步,使用同步代码块
public synchronized void setName(String name) {
if(flag) {
//true说明有面包,让线程等待
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
this.name=name;
count++;
System.out.println("生产者:"+Thread.currentThread().getName()+"---生产了----"+name+count);
flag=true;
this.notify();
}
}
public synchronized void getName() {
if(flag) {
System.out.println("消费者:"+Thread.currentThread().getName()+"---------消费了------"+name+count);
flag=false;
this.notify();
}
else {
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
用唤醒等待机制和判断的好处是,即使一个线程在一次运行 ,也会在判断后进入等待状态。
这里需要注意的是:在使用wait的时候要格外的小心,可能会出现死锁。 并且wait()方法是让线程等待,
notify()是唤醒线程池中的任意一个等待线程(唤醒哪一个是随机的),notifyAll()是唤醒线程池中所有的等待
线程。
线程池:当一个线程被wait()方法后,该线程会保存到线程池中。
当用多线程来实现生产者消费者模式(多个生产者,多个消费者)时:
注意:线程的等待状态是在wait()方法处等待,而不是在run()方法出等待。该代码判断用的是if-else
(不是用的演示视频上的代码),所以不会出现连续生产的情况,但是会出现死锁的情况,因为notify()方法
唤醒的线程是不确定的,当生产者唤醒生产者的时候,就会发生死锁,所以要想确定唤醒对面的线程,只需要
把notify()方法换成notifyAll()方法即可。