Java实现多线程生产者消费者模型及优化方案
生产者-消费者模型是进程间通信的重要内容之一。其原理十分简单,但自己用语言实现往往会出现很多的问题,下面我们用一系列代码来展现在编码中容易出现的问题以及最优解决方案。
/* 单生产者、单消费者生产烤鸭 */
class Resource
{
private String name;
private int count = 1; //计数器,记录有多少只烤鸭被生产及消费
private boolean flag = false; //停止标记
public synchronized void set(String name) //生产烤鸭方法
{
while(flag) //如果flag=true,则等待
{
try{this.wait();}catch(InterruptedException e){} //使用wait()方法必须要捕捉异常
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName()+"..."+this.name);
flag = true; //生产完一个烤鸭就将flag设为true,等待消费者消费烤鸭
notify(); //唤醒消费者
}
public synchronized void out()
{
while(!flag)
{
try{this.wait();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName()+"........."+this.name);
flag = false; //消费完一个烤鸭就将flag设为false,等待生产者生产烤鸭
notify(); //唤醒生产者
}
} class Producer implements Runnable
{
Resource r;
Producer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("kaoya"); //生产烤鸭
}
}
} class Consumer implements Runnable
{
Resource r;
Consumer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out(); //消费烤鸭
}
}
} public class ResourceDemo
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer producer = new Producer(r);
Consumer consumer = new Consumer(r);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(producer);
t1.start();
t2.start();
}
}
以上是单生产者单消费者的代码,我们来看一下运行结果:

然而正如实际情况,饭店的厨房里不可能只有一个厨子,也不可能只有一个顾客,因此只考虑单消费者单生产者是没有意义的。我们再各加入一个消费者和生产者线程。
很显然,运行中出现了死锁,程序卡住,这非常让人费解。
实际上,在加入两个线程时,整个程序的运行过程已经发生了很大的变化。起始flag为false,因此生产者线程t1可以进入同步区,完成后将flag设为true。在这种情况下,生产者线程t3会进入wait状态。与此同时,两个消费者线程t2和t4可能都会因为一开始flag为false进入wait状态。当线程t1完成整个流程准备notify()时,线程池里一共有三个线程:生产者t3,消费者t2、t4。由于notify()的工作机制是随机唤醒一个线程,最不巧的情况,如果它唤醒的是生产者线程t3,那么t3判断flag为true,又会进入wait状态。此时此刻,所有四个线程都会进入wait状态,自然地,发生了线程死锁。
那么如何解决?最简单的方法是将notify()改为notifyAll(),这样每一次都会唤醒所有的线程。然而这种方法实在是有一点浪费资源,因为本来我们只需要唤醒1个或2个线程,而使用notifyAll()则必须将三个线程都唤醒。面试进行到这里,肯定会有一个问题——如何优化?
我们可以使用JDK1.5的特性Lock来处理,在使用它时需要在头部import java.util.concurrent.locks.*;它可以实现仅唤醒消费者线程或生产者线程,赋予了程序员更多的控制权,自然也能提高程序的效率,使得不必唤醒不需要唤醒的线程。具体代码如下:
import java.util.concurrent.locks.*; class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
Lock lock = new ReentrantLock(); //新建锁对象
Condition producer_con = lock.newCondition(); //新建生产者condition
Condition consumer_con = lock.newCondition(); //新建消费者condition
public void set(String name)
{
lock.lock(); //上锁
try
{
while(flag)
{
try{producer_con.await();}catch(InterruptedException e){} //注意在这种情况下要将原来的this.wait()改为producer_con.await
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName()+"..."+this.name);
flag = true;
consumer_con.signal(); //唤醒消费者线程
}
finally
{
lock.unlock(); //把解锁放在finally里,不管中间是否有异常一定要解锁
}
}
public void out()
{
lock.lock();
try
{
while(!flag)
{
try{consumer_con.await();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName()+"........."+this.name);
flag = false;
producer_con.signal(); //唤醒生产者线程
}
finally
{
lock.unlock();
}
}
} class Producer implements Runnable
{
Resource r;
Producer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.set("kaoya");
}
}
} class Consumer implements Runnable
{
Resource r;
Consumer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
} public class ResourceDemo
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer producer = new Producer(r);
Consumer consumer = new Consumer(r);
Thread t1 = new Thread(producer);
Thread t2 = new Thread(consumer);
Thread t3 = new Thread(producer);
Thread t4 = new Thread(consumer);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果,如丝般顺滑:

Java实现多线程生产者消费者模型及优化方案的更多相关文章
- 第23章 java线程通信——生产者/消费者模型案例
		
第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...
 - java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-【费元星Q9715234】
		
java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-[费元星Q9715234] 说明如下,不懂的问题直接我[费元星Q9715234] 1.反射的意义在于不将xml tag ...
 - Java里的生产者-消费者模型(Producer and Consumer Pattern in Java)
		
生产者-消费者模型是多线程问题里面的经典问题,也是面试的常见问题.有如下几个常见的实现方法: 1. wait()/notify() 2. lock & condition 3. Blockin ...
 - Python多线程-生产者消费者模型
		
用多线程和队列来实现生产者消费者模型 # -*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import threading imp ...
 - [多线程] 生产者消费者模型的BOOST实现
		
说明 如果 使用过程中有BUG 一定要告诉我:在下面留言或者给我邮件(sawpara at 126 dot com) 使用boost::thread库来实现生产者消费者模型中的缓冲区! 仓库内最多可以 ...
 - java实现多线程生产者消费者模式
		
1.概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消 ...
 - java并发之生产者消费者模型
		
生产者和消费者模型是操作系统中经典的同步问题.该问题最早由Dijkstra提出,用以演示它提出的信号量机制. 经典的生产者和消费者模型的描写叙述是:有一群生产者进程在生产产品.并将这些产品提供给消费者 ...
 - Java实现多线程生产者消费者模式的两种方法
		
生产者消费者模式:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据.生产者生产一个,消费者消费一个,不断循环. 第一种实现方法,用BlockingQueue阻塞队 ...
 - Java多线程15:Queue、BlockingQueue以及利用BlockingQueue实现生产者/消费者模型
		
Queue是什么 队列,是一种数据结构.除了优先级队列和LIFO队列外,队列都是以FIFO(先进先出)的方式对各个元素进行排序的.无论使用哪种排序方式,队列的头都是调用remove()或poll()移 ...
 
随机推荐
- idea的maven搭建
			
IntelliJ IDEA中创建Web聚合项目(Maven多模块项目) 在file-setting-maven中可以配置自己下载的maven,在自己下载maven目录的config下的setting. ...
 - docx httpheader头设置
			
设置contentType内容类型如下: Extension MIME Type .doc application/msword .dot application/msword .docx appli ...
 - ffplay流程分析
			
void main() { is = stream_open(input_filename, file_iformat); } static VideoState *stream_open(const ...
 - android studio 模拟器无法联网的解决方法
			
本人环境 win10 64位+ android studio 自带的模拟器 1.首先把模拟器打开,然后需要把SDK文件夹下的platform-tools添加到系统变量中(此目录一定要不要弄错). 2 ...
 - a 标签实现分享功能
			
在网页中,经常会用到分享功能,例如分享到qq,分享到微信,分享到微博等,但是怎么实现呢?一直没有想清楚这个问题,觉得好高大上的样子,于是在网上找了一些资料,也没有看出个什么所以然来: 于是有些心急了, ...
 - nginx 做数据仓库时,location  404 Not Found,发现找不到要用的数据报:Not Found
			
背景: 获得远程机器某个目录下的数据文件 方案:使用Nginx配置 1./home/ftp/www/ 下面有images 文件夹,为了访问images下面文件,配置Nginx如下: location ...
 - Linux系统常见的压缩与打包命令
			
常见的压缩文件扩展名 1.*.Z compress程序压缩的文件 2.*.gz gzip程序压缩的文件 3..bz2 bzip2程序压缩的文件 4..t ...
 - BOOST_PREVENT_MACRO_SUBSTITUTION
			
[BOOST_PREVENT_MACRO_SUBSTITUTION] 用于防止函数被macro替换的问题. 例如: 参考: 1.https://blog.csdn.net/yanxiangtianji ...
 - MySQL编码不一致导致查询结果为空
			
升级数据库后(5.1到8.0),发现一个奇怪的问题,某些页面在升级前可以正常查询,但升级后什么也查不出来了,有时候还会查出错误的结果.经过一整天的排查,终于发现由两个原因导致,现记录如下. 第一是数据 ...
 - Python设计模式 - UML - 组合结构图(Composite Structure Diagram)
			
简介 组合结构图用来显示组合结构或部分系统的内部构造,包括类.接口.包.组件.端口和连接器等元素,是UML2.0的新增图. 组合结构图侧重复合元素的方式展示系统内部结构,包括与其他系统的交互接口和通信 ...