Java多线程(2)生产者消费者问题(一)
2017-07-02 18:34
387 查看
一、问题描述
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。如何用代码描述此类问题。二、一个消费者线程、一个生产者线程
有几点需要强调:1.main方法中的资源res的线程t1、t2共享的。所以当操作该资源时需要同步
2.同步函数的锁即为this,所以res即为线程t1、t2的锁
3.因为只有两个线程所以能够准确的唤醒对象线程(即消费者线程唤醒生产者进程、生产者进程唤醒消费者进程),
那么问题就来了,如果是多消费者进程,多生产进程怎么办?
package com.thread.pcprob; /** * 共享数据 * @author dqf * */ public class Resource { //产品编号 private Integer proNo = 0; //判断是消费数据,还是生产数据 private boolean flag = false; //生产数据 public synchronized void produce(){ if(flag){//如果flag=true,则等待消费; try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("生产-->" + (++proNo)); flag = true; this.notify();//唤醒等待进程 } //消费数据 public synchronized void consume(){ if(!flag){//如果flag=false,则等待生产 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费-------->" + proNo); flag = false; this.notify();//唤醒等待进程 } }
package com.thread.pcprob; /** * 生产者 * @author dqf * */ public class Producer implements Runnable{ private Resource res; public Producer(Resource res) { this.res = res; } @Override public void run() { while(true){ res.produce(); } } }
package com.thread.pcprob; /** * 消费者 * @author dqf * */ public class Consumer implements Runnable{ private Resource res; public Consumer(Resource res) { this.res = res; } @Override public void run() { while(true){ res.consume(); } } }
package com.thread.pcprob; public class App { public static void main(String[] args) { Resource res = new Resource(); Producer p = new Producer(res); Consumer c = new Consumer(res); Thread t1 = new Thread(p); Thread t2 = new Thread(c); t1.start(); t2.start(); } }
部分输出:
消费-------->159091 生产-->159092 消费-------->159092 生产-->159093 消费-------->159093 生产-->159094 消费-------->159094 生产-->159095 消费-------->159095 生产-->159096 消费-------->159096 生产-->159097 消费-------->159097 生产-->159098 消费-------->159098 生产-->159099 消费-------->159099
三、多生产者进程、多消费者进程
我们把main方法修改如下:public class App { public static void main(String[] args) { Resource res = new Resource(); Producer p = new Producer(res); Consumer c = new Consumer(res); Thread t1 = new Thread(p); Thread t2 = new Thread(p); Thread t3 = new Thread(c); Thread t4 = new Thread(c); t1.start(); t2.start(); t3.start(); t4.start(); } }
部分输出:
消费-------->123384 消费-------->123384 生产-->123385 消费-------->123385 消费-------->123385 生产-->123386 消费-------->123386 消费-------->123386 生产-->123387 消费-------->123387 消费-------->123387 生产-->123388 消费-------->123388
我们发现会出现两种错误的情况
1.生产一个,消费多次
2.生产多次,消费一次
下面来分析产生两种问题的原因:
假设一种状态:
flag = false,t1获取到锁,t2、t3、t4等待锁释放。
因为flag为false,t1阻塞自动释放锁,t2获取锁,同样因为flag为false,t2阻塞自动释放锁。
若t3获取锁,执行到this.notify(),则会唤醒一个阻塞状态的线程,如果唤醒了t1线程,t1线程结束后又唤醒了t2。
则,就会出现多次生产,一次消费的情况。
其实造成这种情况的主要原因是:不能指定具体唤醒哪一个线程、或者说不能指定唤醒哪一类线程。导致,生产进程结束后,又唤醒了生产进程;或者消费进程结束后,又唤醒消费进程。
如果将Resource改为(if改为while),那么在线程被唤醒后,都会再次判断一次flag。
package com.thread.pcprob; /** * 共享数据 * @author dqf * */ public class Resource { //产品编号 private Integer proNo = 0; //判断是消费数据,还是生产数据 private boolean flag = false; //生产数据 public synchronized void produce(){ while(flag){//如果flag=true,则等待消费; try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("生产-->" + (++proNo)); flag = true; this.notify();//唤醒等待进程 } //消费数据 public synchronized void consume(){ while(!flag){//如果flag=false,则等待生产 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费-------->" + proNo); flag = false; this.notify();//唤醒等待进程 } }
但是这种情况又会导致,t1、t2、t3、t4全部处于阻塞状态。
最终写法(notify改为notifyAll):
package com.thread.pcprob; /** * 共享数据 * @author dqf * */ public class Resource { //产品编号 private Integer proNo = 0; //判断是消费数据,还是生产数据 private boolean flag = false; //生产数据 public synchronized void produce(){ while(flag){//如果flag=true,则等待消费; try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("生产-->" + (++proNo)); flag = true; this.notifyAll();//唤醒等待进程 } //消费数据 public synchronized void consume(){ while(!flag){//如果flag=false,则等待生产 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费-------->" + proNo); flag = false; this.notifyAll();//唤醒等待进程 } }
相关文章推荐
- JAVA多线程经典问题 -- 生产者 消费者
- java多线程总结六:经典生产者消费者问题实现
- java多线程(同步与死锁问题,生产者与消费者问题)
- java多线程总结六:经典生产者消费者问题实现
- java多线程之生产者消费者经典问题 - 很不错的范例
- java多线程之生产者消费者经典问题
- Java多线程之~~~使用Exchanger在线程之间交换数据[这个结合多线程并行会有解决很多问题]生产者消费者模型
- Java多线程--生产者与消费者问题
- Java多线程解决生产者消费者问题
- java多线程——生产者消费者问题
- 关于Java多线程实现生产者和消费者的问题
- Java多线程 生产者消费者问题 (2)
- java多线程(同步和死锁,生产者和消费者问题)
- Java多线程模拟生产者消费者问题
- Java多线程生产者消费者说明等待唤醒机制问题和虚假唤醒问题
- java多线程-生产者消费者经典问题
- Java多线程模拟实现消费者生产者问题
- java多线程(同步与死锁问题,生产者与消费者问题)
- Java多线程: 生产者消费者问题(源码)
- Java多线程解决生产者-消费者问题