生产者消费者模型——线程间的通信方式
我们俩要进行通信,我把数据传给你,但是我们俩之间传数据不是直接传(调用你,给你传数据),而是咱们俩之间有数据容器,我把数据放容器里,你从容器里取数据。
生产者线程:产生数据,并添加到一个集合(数据容器)
消费者线程:从集合(数据容器)中获取(移出)数据,进行运算处理
你在餐厅吃饼,盘子的容量限制是5,放满了不能放
先有盘子,再有厨师(做一张饼就往盘子放饼),再有你(一张一张吃饼)
盘子是栈Stack,最后放的饼会被最先取走,后入先出
public class Stack {
private char[]a=new char[5];
private int index;
public void push(char c){
a[index]=c;
index++;
}
public char pop()
{
index--;
char c=a[index];
return c;
}
public boolean isEmpty(){
return index==0;
}
public boolean isFull(){
return index==5;
}
}
生产者线程Producer
import java.util.Random;
public class Producer extends Thread {
private Stack stack;
public Producer(Stack stack) {
this.stack = stack;
}
@Override
public void run() {
int i = 1;
while (i <= 50) {
// a~z 97+0=a 97+1=b 97+25=z; 97+[0,26)
char c = (char) ('a' + new Random().nextInt(26));
synchronized (stack) {
// 判断是否满
while (stack.isFull()) {
try {
// 暂停,在栈对象上等待,收到通知后会醒过来继续执行
stack.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 继续
}
stack.push(c);
System.out.println("压入(烘烤)<--" + c + "饼");
// 通知在栈对象上等待的线程
stack.notifyAll();
}
i++;
}
}
}
消费者线程Consumer
public class Consumer extends Thread {
private Stack stack;
public Consumer(Stack stack) {
this.stack = stack;
}
@Override
public void run() {
int i = 1;
while (i <= 50) {
synchronized (stack) {
while (stack.isEmpty()) {
try {
stack.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
char c = stack.pop();
System.out.println("弹出(吃掉)-->" + c + "饼");
stack.notifyAll();
}
i++;
}
}
}
测试类
public class Test {
public static void main(String[] args) {
Stack stack=new Stack();
Producer p = new Producer(stack);
Consumer c = new Consumer(stack);
p.start();
c.start();
}
}
运行结果
压入(烘烤)<--m饼
压入(烘烤)<--v饼
压入(烘烤)<--e饼
压入(烘烤)<--n饼
压入(烘烤)<--t饼
弹出(吃掉)-->t饼
弹出(吃掉)-->n饼
弹出(吃掉)-->e饼
弹出(吃掉)-->v饼
弹出(吃掉)-->m饼
压入(烘烤)<--f饼
弹出(吃掉)-->f饼
压入(烘烤)<--j饼
弹出(吃掉)-->j饼
压入(烘烤)<--b饼
弹出(吃掉)-->b饼
压入(烘烤)<--j饼
压入(烘烤)<--r饼
压入(烘烤)<--o饼
压入(烘烤)<--x饼
压入(烘烤)<--f饼
弹出(吃掉)-->f饼
弹出(吃掉)-->x饼
弹出(吃掉)-->o饼
弹出(吃掉)-->r饼
弹出(吃掉)-->j饼
压入(烘烤)<--q饼
压入(烘烤)<--h饼
压入(烘烤)<--d饼
压入(烘烤)<--l饼
压入(烘烤)<--m饼
弹出(吃掉)-->m饼
弹出(吃掉)-->l饼
弹出(吃掉)-->d饼
弹出(吃掉)-->h饼
弹出(吃掉)-->q饼
压入(烘烤)<--x饼
压入(烘烤)<--m饼
压入(烘烤)<--l饼
压入(烘烤)<--t饼
压入(烘烤)<--o饼
弹出(吃掉)-->o饼
弹出(吃掉)-->t饼
弹出(吃掉)-->l饼
弹出(吃掉)-->m饼
弹出(吃掉)-->x饼
压入(烘烤)<--x饼
弹出(吃掉)-->x饼
压入(烘烤)<--v饼
弹出(吃掉)-->v饼
压入(烘烤)<--k饼
压入(烘烤)<--k饼
压入(烘烤)<--k饼
压入(烘烤)<--i饼
压入(烘烤)<--m饼
弹出(吃掉)-->m饼
弹出(吃掉)-->i饼
弹出(吃掉)-->k饼
弹出(吃掉)-->k饼
弹出(吃掉)-->k饼
压入(烘烤)<--z饼
弹出(吃掉)-->z饼
压入(烘烤)<--v饼
压入(烘烤)<--v饼
压入(烘烤)<--l饼
压入(烘烤)<--f饼
压入(烘烤)<--e饼
弹出(吃掉)-->e饼
弹出(吃掉)-->f饼
弹出(吃掉)-->l饼
弹出(吃掉)-->v饼
弹出(吃掉)-->v饼
压入(烘烤)<--d饼
压入(烘烤)<--y饼
压入(烘烤)<--o饼
压入(烘烤)<--m饼
压入(烘烤)<--n饼
弹出(吃掉)-->n饼
弹出(吃掉)-->m饼
弹出(吃掉)-->o饼
弹出(吃掉)-->y饼
弹出(吃掉)-->d饼
压入(烘烤)<--r饼
压入(烘烤)<--w饼
压入(烘烤)<--k饼
压入(烘烤)<--v饼
压入(烘烤)<--o饼
弹出(吃掉)-->o饼
弹出(吃掉)-->v饼
弹出(吃掉)-->k饼
弹出(吃掉)-->w饼
弹出(吃掉)-->r饼
压入(烘烤)<--l饼
压入(烘烤)<--u饼
压入(烘烤)<--q饼
压入(烘烤)<--v饼
弹出(吃掉)-->v饼
弹出(吃掉)-->q饼
弹出(吃掉)-->u饼
弹出(吃掉)-->l饼
等待和通知
没有数据时,消费者等待
数据存满时,生产者等待
生产者放入数据,发出通知notifyAll();
消费者取数据,发出通知notifyAll();
Object的方法-等待方法和通知方法
wait()
线程在调用的对象上等待
notify()
在指定对象上发出通知,通知在该对象上所有线程中的一个
如:a.notify()
notifyAll()
通知所有等待的线程
等待和通知方法必须在同步代码块内执行
synchronized (对象){
等待方法或通知方法
}
wait()外面,通常是循环条件判断
while(stack.isFull()){
try {
stack.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
等待和通知方法,必须通过加锁的对象调用
如果不写,会抛监视器异常
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)