[Java]多线程之生产者消费者
2015-06-07 22:50
483 查看
上一篇多线程写完好久了,今天补上顺便回忆下
前面提到死锁的产生,这里来解决下,换个demo
购票时会有退票,包子有销售也会有生产,所以提一个实际中的线程问题,经典的生产者消费者
生产者、消费者实现runnable接口,由于双方使用同一个资源包子,可以单独拿出来,写上两个属性,价格和名字
现在生产者要生产包子,相当于给资源--包子的属性赋值,贴上标签,说明这个生产好了,消费者可以拿去。
然后消费者要来拿,此时baozi在生产者里是一个成员变量,如何让消费者拿到?其实就是如何共享数据?让他们使用构造方法传递同一个引用,使用同一个引用就共享了同一个数据。
接下来,要在run里拿到包子,输出一下
至此生产和消费框架搭建完毕,去demo里启动线程
生产者消费者都是实现接口的方式,这边首先要分别实例化一个对象出来再去启动线程。因为生产者和消费者都是带参的,没有默认的无参了,这边实例化的时候也必须带上
这个时候走起有问题,一直是消费者被调用,但是没有包子
为啥,因为生产者的run方法内一直在new包子,而不是上面的引用传过来的,这样子生产者和消费的包子其实不是同一个,没有实现共享,去掉new这一行
OK~现在依然有问题,因为在生产里,实际上有两步,名字和价格是分开的,这就有可能在命名后被挂起,消费者拿到的是没有价格的包子,或者在有多种包子的时候,出现错乱
再添加一种包子
看看运行结果
给生产者消费者都上锁,注意锁的选举,必须是同一把锁,这里有现成的对象baozi
再走起,还会出问题,有可能拿到是空的,因为有可能是消费者先走的,这个时候还没有生产。
这个时候同步机制无法解决,他只能解决非原子操作带来的问题,现在就需要进程间通信来搞定~
三个方法,Wait() Notify() notifyAll()
怎么实现通知机制的呢,在走到notify的时候,把锁释放掉,另外一个线程就能进来了
他们都在object类下面,虽然线程的实现依赖于thread类,两个线程间如何实现通信的?是因为拥有同一把锁,这一把锁可以是任何一个对象,任何对象都有锁,所以这些具体的通信方法应该是基于顶层的object类,而不能局限在thread里
要解决这种问题:最优的解决方案是:
*
生产者来说: 看看有没有包子,有的话就等待,没有的话才生产,生产完之后发出通知
*
消费者来说: 看看有没有包子,有的话就买,没有的话就等待。买了之后就发出通知
给包子再加一个属性,flag标志位,默认为false,在生产者里面判断下有没有包子
给生产者和消费者要加上flag,一定要注意,通过flag判断有没有包子必须在synchronized()锁里面,否则在判断flag的时候,有可能被挂起,因为这不是原子操作,这会导致后面的进程间通信的几个方法出现异常。
对于消费者来说,有包子才消费,消费了还要通知生产者,生产者还在等待;没有包子就等待
对于生产者来说,有包子则等待,等待消费者消费后来的通知;没有包子则生产,生产好了通知消费者有包子了,消费者还在等待
OK,走起,至此这个例子基本搞定
完整代码:
生产者
消费者
资源池:
main:
前面提到死锁的产生,这里来解决下,换个demo
购票时会有退票,包子有销售也会有生产,所以提一个实际中的线程问题,经典的生产者消费者
生产者、消费者实现runnable接口,由于双方使用同一个资源包子,可以单独拿出来,写上两个属性,价格和名字
现在生产者要生产包子,相当于给资源--包子的属性赋值,贴上标签,说明这个生产好了,消费者可以拿去。
然后消费者要来拿,此时baozi在生产者里是一个成员变量,如何让消费者拿到?其实就是如何共享数据?让他们使用构造方法传递同一个引用,使用同一个引用就共享了同一个数据。
接下来,要在run里拿到包子,输出一下
至此生产和消费框架搭建完毕,去demo里启动线程
生产者消费者都是实现接口的方式,这边首先要分别实例化一个对象出来再去启动线程。因为生产者和消费者都是带参的,没有默认的无参了,这边实例化的时候也必须带上
这个时候走起有问题,一直是消费者被调用,但是没有包子
为啥,因为生产者的run方法内一直在new包子,而不是上面的引用传过来的,这样子生产者和消费的包子其实不是同一个,没有实现共享,去掉new这一行
OK~现在依然有问题,因为在生产里,实际上有两步,名字和价格是分开的,这就有可能在命名后被挂起,消费者拿到的是没有价格的包子,或者在有多种包子的时候,出现错乱
再添加一种包子
看看运行结果
给生产者消费者都上锁,注意锁的选举,必须是同一把锁,这里有现成的对象baozi
再走起,还会出问题,有可能拿到是空的,因为有可能是消费者先走的,这个时候还没有生产。
这个时候同步机制无法解决,他只能解决非原子操作带来的问题,现在就需要进程间通信来搞定~
三个方法,Wait() Notify() notifyAll()
怎么实现通知机制的呢,在走到notify的时候,把锁释放掉,另外一个线程就能进来了
他们都在object类下面,虽然线程的实现依赖于thread类,两个线程间如何实现通信的?是因为拥有同一把锁,这一把锁可以是任何一个对象,任何对象都有锁,所以这些具体的通信方法应该是基于顶层的object类,而不能局限在thread里
要解决这种问题:最优的解决方案是:
*
生产者来说: 看看有没有包子,有的话就等待,没有的话才生产,生产完之后发出通知
*
消费者来说: 看看有没有包子,有的话就买,没有的话就等待。买了之后就发出通知
给包子再加一个属性,flag标志位,默认为false,在生产者里面判断下有没有包子
给生产者和消费者要加上flag,一定要注意,通过flag判断有没有包子必须在synchronized()锁里面,否则在判断flag的时候,有可能被挂起,因为这不是原子操作,这会导致后面的进程间通信的几个方法出现异常。
对于消费者来说,有包子才消费,消费了还要通知生产者,生产者还在等待;没有包子就等待
对于生产者来说,有包子则等待,等待消费者消费后来的通知;没有包子则生产,生产好了通知消费者有包子了,消费者还在等待
OK,走起,至此这个例子基本搞定
完整代码:
生产者
public class Producer implements Runnable { Baozi baozi; int x =0; public Producer(Baozi b){ baozi = b; } @Override public void run() { while(true){ synchronized (baozi) { if(!baozi.flag){ if(x%2==0){ baozi.name="天津狗不理"; baozi.price=10; } else{ baozi.name="澳门猪扒包"; baozi.price=20; } x++; //没有包子,生产,此时就应该有了,改为true baozi.flag=true; //通知消费者有包子 baozi.notify(); System.out.println("Producer.sell " +baozi.name+" "+baozi.price); }else{ //flag为true,说明有包子,此时要等待消费者来消费 try { baozi.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } }
消费者
public class Customer implements Runnable { Baozi baozi; public Customer(Baozi b){ baozi = b; } @Override public void run() { while (true) { synchronized (baozi) { if(baozi.flag){ System.out.println("Customer got "+ baozi.name+" "+baozi.price ); //消费了,置为false baozi.flag=false; //给生产者通知,因为你是有才来消费 //而生产者有则是等待,不通知一直等 baozi.notify(); }else{ try { baozi.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } }
资源池:
public class Baozi { String name; int price; boolean flag=false; }
main:
public class MyPandCDemo { public static void main(String[] args) { // TODO Auto-generated method stub Baozi baozi =new Baozi(); //需要带参来实例化 Producer producer = new Producer(baozi); Customer customer = new Customer(baozi); Thread thread1 = new Thread(producer, "Producer"); Thread thread2 = new Thread(customer, "Customer"); thread1.start(); thread2.start(); } }
相关文章推荐
- java.sql.SQLException: Parameter index out of range (0 < 1 )
- JavaWeb学习02-03
- Java获取图像Exif信息
- Java:基本语法
- java n皇后
- Java——(一)一切都是对象
- java安装及设置环境变量
- 有关eclipse查看应用签名的问题
- Java IDL介绍
- Java 接口和抽象类区别
- Java I/O— 梳理各种“流”
- Java中Properties类
- 为什么要读这本书《java编程思想》
- 如何把 JDK doc 文档与 Eclipse 关联?
- 读书笔记之java编程思想2
- java中间件学习1-java中间件的定义
- Java核心技术卷一学习笔记1
- java数组
- 第二十二篇 Java的一些关键字、 作用域 还有运算符的使用
- Spring MVC工作流程图