整理整理生产者消费者模式,用通俗的话描述

来源:互联网 发布:手机语音拨号软件 编辑:程序博客网 时间:2024/06/16 00:40

之前学习多线程问题遇到的最大的难度就是,很多;生产者消费者模式是比较经典的多线程问题,看似 不难,但实际上有很多地方值得注意的。

首先是几个问题


问题1 一共有哪些对象?

生产者与消费者是肯定有的,生产者与消费者之间还有一个缓冲区对象,用以保存生产与消费的目标,还有一个对象就是主线程对象,用来运行多个线程的。

        追问:为什么要有一个缓冲区对象?

        答:为了实现生产者与消费者解耦,互补依赖或者关联。

        追问:每个对象都包含哪些变量与方法?

        答:生产者包含缓冲区对象并在run()方法里面执行生产调度命令;消费者含缓冲区对象并在run()方法里面执行消费调度命令;缓冲区则保存产品的数组/Stack/Queue以及生产与消费的具体指令;主线程对象则用来实现生产者与消费者线程创建与运行的。


问题2 哪些方法需要同步?

缓冲区对象的生产与消费必须实现同步。


我在进行研究的时候,会出现如下错误的代码:

/** *  项目名称: *  文件说明: *  主要特点:生产者消费者模式 *  版本号:1.0 *  制作人:lcx *  创建时间:2013-12-3 **/package treadgame;import java.util.Stack;/** * @author lcx * */public class Producer_Consumer1 {public static void main(String[] args) {Stack<String> apples=new  Stack<String>();Thread td1=new Thread(new Producer1("生产者1",apples));Thread td2=new Thread(new Consumer1("消费者",apples));td2.start();td1.start();}}class Producer1 implements Runnable{String name;Stack<String> apples; public Producer1(String name,Stack<String> apples){this.name=name;this.apples=apples;}public synchronized void produce(String apple) throws Exception{//System.out.println("生产者 此时一共"+apples.size()+"个APPLE");if(apples.size()>=5){System.out.println(name+"阻塞");wait();}notify();apples.push(apple);System.out.println(name+"已经生产了"+apple);}public void run() {for(int i=0;i<10;i++){try {produce("苹果"+i);} catch (Exception e) {e.printStackTrace();}}}}class Consumer1 implements Runnable{String name;Stack<String> apples; public Consumer1(String name,Stack<String> apples){this.name=name;this.apples=apples;}public synchronized void consume() throws Exception{//System.out.println("消费者 此时一共"+apples.size()+"个APPLE");if(apples.size()==0){System.out.println(name+"阻塞");wait();}notify();String apple=apples.pop();System.out.println(name+"已经消费了"+apple);}public void run() {for(int i=0;i<10;i++){try {consume();} catch (Exception e) {e.printStackTrace();}}}}

然后一直会出现死锁,仔细思考后发现wait与notify出现的位置不正确,wait的作用是执行wait的线程阻塞,然后释放对象锁;notify是唤醒改对象的其他线程。所以wait方法与notify应该是对应的一个对象,而我在代码中却希望通过在Consumer中的wait唤醒Producer的对象,肯定是不对的。

结果修改后,如下

/** *  项目名称: *  文件说明: *  主要特点:生产者消费者模式 *  版本号:1.0 *  制作人:lcx *  创建时间:2013-12-3 **/package treadgame;import java.util.Stack;/** * @author lcx * */public class Producer_Consumer2 {public static void main(String[] args) {Resturant res=new Resturant();Thread td1=new Thread(new Producer2(res));//Thread td2=new Thread(new Producer("生产者2",apples));//Thread td3=new Thread(new Producer("生产者3",apples));Thread td4=new Thread(new Consumer2(res));td4.start();td1.start();//td2.start();//td3.start();}}class Resturant{Stack<String> apples=new  Stack<String>();public synchronized void produce(String apple) throws Exception{//System.out.println("生产者 此时一共"+apples.size()+"个APPLE");if(apples.size()>=5){System.out.println("生产阻塞");wait();}notify();apples.push(apple);System.out.println("已经生产了"+apple);}public synchronized void consume() throws Exception{//System.out.println("消费者 此时一共"+apples.size()+"个APPLE");if(apples.size()==0){System.out.println("消费阻塞");wait();}notify();String apple=apples.pop();System.out.println("已经消费了"+apple);}}class Producer2 implements Runnable{Resturant res;public Producer2(Resturant res){this.res=res;}public void run() {for(int i=1;i<=10;i++){try {res.produce("苹果"+i);} catch (Exception e) {e.printStackTrace();}}}}class Consumer2 implements Runnable{Resturant res;Stack<String> apples; public Consumer2(Resturant res){this.res=res;}public void run() {for(int i=1;i<=10;i++){try {res.consume();} catch (Exception e) {e.printStackTrace();}}}}

这样就可以了。


问题3 若produce与consume方法都不是同步方法,会出现什么问题?

会出现IllegalMonitorStateException异常,因为wait必须在同步代码块中。


总结:

就我现在的经验看来,分析多线程问题,应该分析的是资源,而不是线程。因为线程的执行是由他们与资源的关系所决定的。


        

1 0
原创粉丝点击