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()移 ...
随机推荐
- oracle SQL多表查询
SQL多表查询 1.集合理论 1.1 什么是集合 具有某种特定性质的事物的总体. 集合的特性:无序性.互异性.确定性. 一个集合可以小到从一个表中取出一行中的一列. 1 ro ...
- asp.net ajax get 调用(和post不一样,直接返回json才行,否则报错;post不能返回json)
<script type="text/javascript" > $(document).ready(function () { $('#Label1').click( ...
- Django ORM queryset object 解释(子查询和join连表查询的结果)
#下面两种是基于QuerySet查询 也就是说SQL中用的jion连表的方式查询books = models.UserInfo.objects.all() print(type(books)) --- ...
- MM-移动类型
链接:SAP移动类型 移动类型 备注 业务类型 SAP中事务代码 备注 101 采购订单收货.生产订单收货 收货 migo CO11N顶层处理移动类型\跨工厂收货 102 采购订单收货冲销 收货 ...
- K数和问题
问题描述 给定n个不同的正整数(数组num),整数k(k < = n)以及一个目标数字target.在这n个数里面找出k个数,使得这k个数的和等于目标数字,求问有多少种方案? 解决思路 该类问题 ...
- PuTTY窗口看不到鼠标解决方法
PuTTY终端工具无法清晰看到鼠标如何处理?通常是因为PuTTY背景色和鼠标颜色相近导致.这里介绍一种通过调整鼠标状态解决方法.说明:文末附PuTTY最新版本下载地址. 操作步骤(Windows10) ...
- 【18/12/31】hashcat源码粗读 --- sha256部分
还没有详细研究过sha256算法的详细原理,主要是移植cf10算法时,hashcat在cf10_parse_hash时并不是直接调用sha256_update和sha256_final, 而是为了pr ...
- c#实现文件写入数据表/以二进制流保存到数据库,并实现下载
上传: 1.上传文件先保存到服务器 File.SaveAs(path) 2.sql(文件和sql在一个服务器上)进行保存操作: insert into File(filename,filebody ...
- Oracle获取一周前,一个月前,一年前, 本周,本月,当年的日期
1.获取当前时间一周前的日期 ' day from dual 类似的 --当前时间减去7分钟的时间 ' MINUTE from dual --当前时间减去7小时的时间 ' hour from dual ...
- CSRF 和 XSS 的区别
XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任 XSS: 跨站脚本攻击 原名为Cross Site Scriptin,为避免和网页层级样式表概念混淆, 另名为XSS ...