Java多线程通信-利用传统的线程通信wait(),notify()方法实现“生产者消费者模式”

时间:2021-10-12 17:29:29

想利用传统的线程通信wait(),notify(),notifyAll()方法,必须依赖于同步监听器的存在,也就是说,对于synchronized修饰的同步方法,因为该类的默认实例(this)就是同步监听器,所以可以在同步方法中直接调用这三个方法;对于使用synchronized修饰的同步代码块,同步监听器是synchronized后括号的对象,所以必须使用该对象调用这三个方法。这三个方法的作用分别是:

wait():导致当前线程等待,直到其它线程调用该同步监听器的notify()方法或notifyAll()方法来唤醒该线程。

notify():唤醒在该同步监听器上等待的单个线程。如果有不至一个线程在该同步监听器上等待,则任意选择唤醒其中一个线程。

notifyAll():唤醒在该同步监听器上等待的所有线程。

下面通过利用传统的线程通信来实现一个典型的“生产者消费者模式”:

1.同一时间内只能有一个生产者生产

2.同一时间内只能有一个消费者消费

3.生产者生产的同时消费者不能消费 

4.消费者消费的同时生产者不能生产 

5.共享空间空时消费者不能继续消费

6.共享空间满时生产者不能继续生产




/**
* @author newuser
*馒头类,有一个编号,用来标记是第几个馒头
*/
class ManTou {
private int num;

public ManTou(int num) {
super();
this.num = num;
}

/* (non-Javadoc)
* @see java.lang.Object#toString()
* 重写toString()方法
*/
@Override
public String toString() {
return "ManTou [num=" + num + "]";
}
}

/**
* @author newuser
*商店类
*/
class Store {
//BlockingQueue<ManTou> manTouQueues = new ArrayBlockingQueue<ManTou>(10);
//定义一个ManTou数组,做共享空间,用来存放ManTou类对象,也就是用来存储馒头
private ManTou[] manTous=new ManTou[10];
//定义一个下标,用来标记数组的存放位置
int index=0;

/**
* @param manTou
* 生产一个馒头manTou,synchronized是同步方法的修饰符,这里同步方法的作用等同于lock
*/
public synchronized void put(ManTou manTou) {
//如果下标超出数组的长度,则把线程推到阻塞状态,不能进行生产了
if (index>=manTous.length) {
try {
wait();//把线程推到阻塞状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//否则(未超出数组的长度),把馒头加到数组里面
manTous[index]=manTou;
System.out.println(Thread.currentThread().getName()+" 生产:"+manTous[index]);
index++;

//唤醒其他线程,这里主要是唤醒消费线程,表示可以进行消费了
notifyAll();
}

/**
* @return ManTou
* 卖出了一个馒头,synchronized是同步方法的修饰符,这里同步方法的作用等同于lock
*/
public synchronized ManTou poll() {
ManTou mTou = null;
//如果下标小于或等于0,也就是库存里没有库存了,则把本线程推到阻塞状态,不能进行消费了
if (index<=0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//库存下标减一
index--;
System.out.println(Thread.currentThread().getName()+" 消费:"+manTous[index]);

//唤醒其他线程,这里主要是唤醒生产线程,表示可以进行生产费了
notifyAll();
return mTou;
}

}

/**
* @author newuser
*生存线程类
*/
class ProduceThreat implements Runnable {

//定义一个Store对象
private Store myStore;

//本类构造器
public ProduceThreat(Store myStore) {
super();
this.myStore = myStore;
}

//重写Runnable的run()方法,也就是线程的执行体
public void run() {
//生产30次
for (int i = 0; i < 30; i++) {
ManTou manTou = new ManTou(i);
myStore.put(manTou);
}
}
}

/**
* @author newuser
*消费者线程类
*/
class ConsumerThreat implements Runnable {

//定义一个Store对象
private Store myStore;

//本类构造器
public ConsumerThreat(Store myStore) {
super();
this.myStore = myStore;
}

//重写Runnable的run()方法,也就是线程的执行体
public void run() {
//消费30次
for (int i = 0; i < 30; i++) {
myStore.poll();
}
}
}

public class ProdeceConsumerTest {

public static void main(String[] args) {

//创建一个Store对象
Store myStore=new Store();

//把Store对象分别传给两个线程
ProduceThreat p=new ProduceThreat(myStore);
ConsumerThreat c=new ConsumerThreat(myStore);

//创建两个子线程
Thread pThread=new Thread(p);
Thread cThread=new Thread(c);

//开始两个线程
pThread.start();
cThread.start();
}

}