一面遇到的,没有想明白,明天看完医生去思考下
不知当时不知道有没有理解正确,只是一脸懵逼,不知道什么意思,下面用java来补一下这个方面的知识。
1. 单个生产者,单个消费者
在生产者/消费者模型中,生产者Producer负责生产数据,而消费者Consumer负责使用数据。多个生产者线程会在同一时间运行,生产数据,并放到内存中一个共享的区域。期间,多个消费者线程读取内存共享区,消费里面的数据。
分析
在下面Java应用程序中,生产者线程向一个线程安全的堆栈缓冲区中写(PUSH)数据,消费者从该堆栈缓冲区中读(POP)数据,这样,这个程序中同时运行的两个线程共享同一个堆栈缓冲区资源。
类Producer是生产者模型,其中的run方法中定义了生产者线程所做的操作,循环调用push()方法,将生产的100个字母送入堆栈中,每次执行完push操作后,调用sleep方法睡眠一段随机时间。
类Consumer是消费者模型,循环调用pop方法,从堆栈取出一个字母,一共取100次,每次执行完push操作后,调用sleep方法睡眠一段随机时间
同步堆栈类SynchronizedStack
package thread;
/***
* 同步栈
* @author XieKai
*
*/
public class SynchronizedStack {
private int index = 0;
private int size = 100;
private char[] data;
public SynchronizedStack(int size) {
System.out.println("创建栈");
this.size = size;
data = new char[size];
}
public synchronized void push(char c) {
while (index == size) {
try {
System.out.println("栈满了");
this.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
data[index] = c;
index++;
this.notify();//通知其他线程
}
public synchronized char pop() {
while(index == 0) {
try {
System.out.println("栈空了");
this.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
index--;
char ch = data[index];
this.notify();
return ch;
}
public synchronized void print() {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i]);
}
System.out.println();
this.notify();//通知其他线程显示堆栈内容
}
}
生产者
package thread;
public class Producer implements Runnable{
private SynchronizedStack stack;
public Producer(SynchronizedStack stack) {
this.stack = stack;
}
@Override
public void run() {
char ch;
for (int i = 0; i < 100; i++) {
ch = (char) (Math.random()*26+'A');
stack.push(ch);
System.out.println("Produced:" + ch);
try {
Thread.sleep((int)(Math.random() *1000));
} catch (InterruptedException e) {
}
}
}
}
消费者
package thread;
public class Consumer implements Runnable {
private SynchronizedStack stack;
public Consumer(SynchronizedStack stack) {
this.stack = stack;
}
@Override
public void run() {
char ch;
for (int i = 0; i < 100; i++) {
ch = stack.pop();
System.out.println("Consumed:" + ch);
try {
Thread.sleep((int)(Math.random()*1000));
} catch (InterruptedException e) {
}
}
}
}
测试类
package thread;
public class ProductConsumerTest {
public static void main(String[] args) {
// 下面的消费者类对象和生产者类对象所操作的是同一个同步堆栈对象
SynchronizedStack stack = new SynchronizedStack(5);
Runnable source = new Producer(stack);
Runnable sink = new Consumer(stack);
Thread t1 = new Thread(source);
Thread t2 = new Thread(sink);
t1.start();
t2.start();
}
}
在本例中,使用了一个生产者线程和一个消费者线程,当生产者线程往堆栈中添加字符时,如果堆栈已满,通过调用this.wait方法,(这里,this就是互斥锁)把自己加入到互斥锁对象(SynchronizedStack)的锁等待队列中,如果该堆栈不满,则该生产者线程加入到互斥锁对象(SynchronizedStack)的锁申请队列中,并且很快就被JVM取出来执行。
当生产者线程在执行添加字符操作的时候,消费者是不能从中取出字符的,只能在等待队列中等待,当生产者添加完字符的时候,使用this.notify()(这里,this就是互斥锁)把等待队列中的第一个消费者唤醒,把它加入到锁申请队列中,很快该消费者线程就会获得CPU时间。此时的生产者线程已经无法再次添加字符,因为消费者线程正在synchronized代码块中运行,JVM把生产者线程加入锁等待队列中。当消费者线程从堆栈中取完字符后,再使用this.notify()方法把生产者从等待进程中唤醒,添加字符,如此循环往复,直到生产者线程和消费者线程都运行结束。