工作2年多来一直也没有计划写自己的技术博客,最近辞职在家翻看《thingking in JAVA》,偶尔看到了生产者与消费者的一个经典的多线程同步问题。本人在工作中很少使用到多线程以及高并发方面的知识,正好回忆下以前学过的一些知识和代码,以经典的多线程问题生产者消费者问题为开篇。

先上一段经典的生产者与消费者的代码

class Product {
private final int orderNum; public Product(int orderNum) {
this.orderNum = orderNum;
} public String toString() {
return "Product" + orderNum;
}
} class Consumer implements Runnable {
private Main main; public Consumer(Main m) {
this.main = m;
} public void run() {
try {
while (!Thread.interrupted()) {
synchronized (this) {
while (main.product == null)
wait();
}
System.out.println("Consumer got " + main.product); synchronized (main.producter) {
main.product = null;
main.producter.notifyAll();
}
}
} catch (InterruptedException e) {
System.out.println("Consumer interrupted!");
} } } class Producter implements Runnable {
private Main main;
private int count = 0; public Producter(Main m) {
this.main = m;
} public void run() {
try {
while (!Thread.interrupted()) {
synchronized (this) {
while (main.product != null)
wait();
}
if (++count == 10) {
System.out.println("Out of product ,closing!");
main.exec.shutdownNow();
}
System.out.print("OK put it up!! ");
synchronized (main.consumer) {
main.product = new Product(count);
main.consumer.notifyAll();
}
TimeUnit.MICROSECONDS.sleep(100);
}
} catch (InterruptedException e) {
System.out.println("Producter interrupted!");
} }
} public class Main {
Product product;
Consumer consumer = new Consumer(this);
Producter producter = new Producter(this); ExecutorService exec = Executors.newCachedThreadPool(); public Main() {
exec.execute(producter);
exec.execute(consumer);
} public static void main(String[] args) {
new Main();
}
}

运行结果如下:

OK put it up!! Consumer got Product1
OK put it up!! Consumer got Product2
OK put it up!! Consumer got Product3
OK put it up!! Consumer got Product4
OK put it up!! Consumer got Product5
OK put it up!! Consumer got Product6
OK put it up!! Consumer got Product7
OK put it up!! Consumer got Product8
OK put it up!! Consumer got Product9
Out of product ,closing!
OK put it up!! Consumer interrupted!
Producter interrupted!

没什么好说的,基本上只要学过JAVA多线程的朋友对于以上代码都会清楚明白。

问题:如果在增加一个角色 这个角色在消费者得到一个产品后把产品销毁,然后再让生产者生产产品,这就好像一个生产者--中间商--消费者的问题,那么如果不用队列的思想,又该怎样解决呢?

分析:此时给product 类增加了一个boolean状态,标识一下是否从中间商处理过 public boolean isDo = false// false 没有处理过,true 已经处理过

那么消费者在拿商品的时候就应该多家一下判断,如果商品没有或者还没有被处理过,那么 就必须等待...

经过此番分析  将以上代码简单的修改了一下

package test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; class Product {
private final int orderNum;
public boolean isDo; // false没有处理过 public Product(int orderNum) {
this.orderNum = orderNum;
isDo = false;
} public String toString() {
return "Product" + orderNum;
}
} class Middleman implements Runnable{
private Main main; public Middleman(Main m) {
this.main = m;
} public void run(){
try {
while (!Thread.interrupted()) {
synchronized (this) {
while (main.product == null || (main.product !=null &&main.product.isDo))
wait();
}
System.out.println("Middleman Got " + main.product+" and do "+main.product);
main.product.isDo = true; //中间商处理
synchronized (main.consumer) {
if(main.product != null)
main.consumer.notifyAll();
}
}
} catch (InterruptedException e) {
System.out.println("Middleman interrupted!");
} }
} class Consumer implements Runnable {
private Main main; public Consumer(Main m) {
this.main = m;
} public void run() {
try {
while(!Thread.interrupted()){
synchronized (this) {
if(main.product == null)
wait();
} //如果生产者已经生产完毕 并且中间商已经处理过了
if(main.product !=null && main.product.isDo){
System.out.println("Consumer Got "+main.product);
}
synchronized (main.producter) {
if(main.product!= null){
main.product = null;
main.producter.notifyAll();
}
}
}
} catch (InterruptedException e) {
System.out.println("Consumer interrupted!");
}
} } class Producter implements Runnable {
private Main main;
private int count = 0; public Producter(Main m) {
this.main = m;
} public void run() {
try {
while (!Thread.interrupted()) {
synchronized (this) {
while (main.product != null)
wait();
}
if (++count == 10) {
System.out.println("Out of product ,closing!");
main.exec.shutdownNow();
}
System.out.print(" The Product is OK ,middleman put it up!! ");
synchronized (main.middleman) {
main.product = new Product(count);
main.middleman.notifyAll();// 此时 唤起的应该是中间商的线程
}
TimeUnit.MICROSECONDS.sleep(100);
}
} catch (InterruptedException e) {
System.out.println("Producter interrupted!");
} }
} public class Main {
Product product;
Consumer consumer = new Consumer(this);
Producter producter = new Producter(this);
Middleman middleman = new Middleman(this); ExecutorService exec = Executors.newCachedThreadPool(); public Main() {
exec.execute(producter);
exec.execute(middleman);
exec.execute(consumer);
} public static void main(String[] args) {
new Main();
}
}

运行结果如下:

The Product is OK ,middleman put it up!! Middleman Got Product1 and do Product1
Consumer Got Product1
The Product is OK ,middleman put it up!! Middleman Got Product2 and do Product2
Consumer Got Product2
The Product is OK ,middleman put it up!! Middleman Got Product3 and do Product3
Consumer Got Product3
The Product is OK ,middleman put it up!! Middleman Got Product4 and do Product4
Consumer Got Product4
The Product is OK ,middleman put it up!! Middleman Got Product5 and do Product5
Consumer Got Product5
The Product is OK ,middleman put it up!! Middleman Got Product6 and do Product6
Consumer Got Product6
The Product is OK ,middleman put it up!! Middleman Got Product7 and do Product7
Consumer Got Product7
The Product is OK ,middleman put it up!! Middleman Got Product8 and do Product8
Consumer Got Product8
The Product is OK ,middleman put it up!! Middleman Got Product9 and do Product9
Consumer Got Product9
Out of product ,closing!
The Product is OK ,middleman put it up!! Consumer interrupted!
Middleman interrupted!
Producter interrupted!

这里补充说明:Main是三个角色的焦点,三个角色必须围绕着Main运行,以便于拿取main.product.

为什么使用notifyAll ?

多个任务在某个特定对象锁上等待,因此有可能我们不知道哪个任务应该被唤醒,因此用notifyAll更加安全些。

为什么要加isDo 真的是逻辑上需要吗?

出了逻辑上的需要还有一个更重要的问题就是,在并发中,某个其他任务很可能会在中间商被唤醒的时候突然插足并且拿走product 导致了中间商一遍又一遍的拿到的都是同一个产品进行处理,而直到消费者把产品拿走,并且唤醒了生产者,才会去拿取下一件商品。所以需要在while(?){wait();}?中加以判断。

使用的是JAVASE5 以后的创建线程的方式---线程池。

这里我用了一些很有意思的东西,在product角色中当生产到10个的时候 调用了exec.showdownNow()方法,该方法向所有的任务发送interrupt,但是在product中任务没有在获得interrupt后关闭,为啥呢? 因为当一个任务视图进入一个阻塞的状态时,这个操作只能抛出InterruptedExctption,所以我们看到了最后的The Product is OK ,middleman put it up!! Consumer interrupted! 然后再调用sleep方法的时候抛出了InterruptedExctption 这也就解释了为什么没有直接结束而是抛出了异常后打印Producter interrupted! 可以推导出 如果我们去掉了sleep方法会是什么样呢?应该可以正常结束了吧

去掉sleep方法的结果

The Product is OK ,middleman put it up!! Middleman Got Product1 and do Product1
Consumer Got Product1
The Product is OK ,middleman put it up!! Middleman Got Product2 and do Product2
Consumer Got Product2
The Product is OK ,middleman put it up!! Middleman Got Product3 and do Product3
Consumer Got Product3
The Product is OK ,middleman put it up!! Middleman Got Product4 and do Product4
Consumer Got Product4
The Product is OK ,middleman put it up!! Middleman Got Product5 and do Product5
Consumer Got Product5
The Product is OK ,middleman put it up!! Middleman Got Product6 and do Product6
Consumer Got Product6
The Product is OK ,middleman put it up!! Middleman Got Product7 and do Product7
Consumer Got Product7
The Product is OK ,middleman put it up!! Middleman Got Product8 and do Product8
Consumer Got Product8
The Product is OK ,middleman put it up!! Middleman Got Product9 and do Product9
Consumer Got Product9
Out of product ,closing!
The Product is OK ,middleman put it up!! Middleman interrupted!
Consumer interrupted!

当已经不生产第10个产品了 (哦 这里多打印了一句话)线程结束了,producter 结束,middlenman结束,此时consmer还在等待middleman的product,但是exec向他发起了interruped  所以抛出异常结束了!

其实经典的生产者和消费者问题可以用队列存储的思想解决,今后在写如果大家有好例子,回复在评论上,并讨论,不胜感激啊。

JAVA多线程经典问题 -- 生产者 消费者的更多相关文章

  1. JAVA多线程经典问题 -- 生产者 消费者 同步队列实现方法

    在JAVASE5 中的java.util.concurrent.BlockingQueue支持,BlockingQueue是一个接口但是我们通常可以使用LinkedBlockingQueue,它是一个 ...

  2. Java多线程-并发协作(生产者消费者模型)

    对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的.就像学习每一门编程语言一样,Hello World!都是最经典的例子. 实际上,准确说应该是“生产者-消费者-仓储”模型,离开了仓 ...

  3. java 多线程 22 :生产者/消费者模式 进阶 利用await()/signal()实现

    java多线程15 :wait()和notify() 的生产者/消费者模式 在这一章已经实现了  wait/notify 生产消费模型 利用await()/signal()实现生产者和消费者模型 一样 ...

  4. Java多线程14:生产者/消费者模型

    什么是生产者/消费者模型 一种重要的模型,基于等待/通知机制.生产者/消费者模型描述的是有一块缓冲区作为仓库,生产者可将产品放入仓库,消费者可以从仓库中取出产品,生产者/消费者模型关注的是以下几个点: ...

  5. JAVA多线程编程之生产者消费者模式

    Java中有一个BlockingQueue可以用来充当堵塞队列,下面是一个桌面搜索的设计 package net.jcip.examples; import java.io.File; import ...

  6. java线程基础巩固---多线程下的生产者消费者模型,以及详细介绍notifyAll方法

    在上一次[http://www.cnblogs.com/webor2006/p/8419565.html]中演示了多Product多Consumer假死的情况,这次解决假死的情况来实现一个真正的多线程 ...

  7. Linux 进程间通信(包含一个经典的生产者消费者实例代码)

    前言:编写多进程程序时,有时不可避免的需要在多个进程之间传递数据,我们知道,进程的用户的地址空间是独立,父进程中对数据的修改并不会反映到子进程中,但内核是共享的,大多数进程间通信方式都是在内核中建立一 ...

  8. 使用Java的BlockingQueue实现生产者-消费者

    http://tonl.iteye.com/blog/1936391 使用Java的BlockingQueue实现生产者-消费者 博客分类: Java JavaBlockingQueue阻塞队列  B ...

  9. 再谈多线程模型之生产者消费者(总结)(c++11实现)

    0.关于 为缩短篇幅,本系列记录如下: 再谈多线程模型之生产者消费者(基础概念)(c++11实现) 再谈多线程模型之生产者消费者(单一生产者和单一消费者)(c++11实现) 再谈多线程模型之生产者消费 ...

随机推荐

  1. Net平台下的消息队列介绍

    Net平台下的消息队列介绍   本系列主要记录最近学习消息队列的一些心得体会,打算形成一个系列文档.开篇主要介绍一下.Net平台下一些主流的消息队列框架.       RabbitMQ:http:// ...

  2. ExtJs--15--Ext.is*各种类型推断的方法,简单看源代码就能够明确了

    /** * Returns true if the passed value is empty, false otherwise. The value is deemed to be empty if ...

  3. 《自己动手写CPU》写书评获赠书活动结果

    <自己动手写CPU>写书评获赠图书的读者有: 京东:8***2.16号哨兵.magicyu.kk6803.jddickyd.杰出的胡兵 亚马逊:徐贺.马先童.jaychen.farmfar ...

  4. JS时间戳比较大小:对于一组时间戳(开始时间~结束时间)和另一组时间戳进行比较,用于判断被比较时间戳组是否在要求范围内

    /* *JS时间戳比较大小:对于一组时间戳(开始时间~结束时间)和另一组时间戳进行比较,用于判断被比较时间戳组是否在要求范围内 *@param date1 date2(形如:'2015-01-01'类 ...

  5. java之集合框架使用细节及常用方法

    集合类的由来:   对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定.  就使用集合容器进行存储. 集合特点: 1,用于存储对象的容器. 2,集合的长度是可变的. 3,集合中不可以存储基本 ...

  6. HTML5它contenteditable属性

    HTML5它contenteditable属性 1.功能说明 (1)功能:同意用户编辑元素中的内容 (2)说明:是一个布尔值.false是不能编辑,true为可编辑 2.分析实例 (1)content ...

  7. 注意,WebDeploy服务会占用80端口。(Windows关闭了IIS,80端口任然被占用)

    最近遇到一个很奇怪的事情,Windows上的 IIS 网站 全关掉了,80端口仍然被占用.然后我新装了一台服务器,一个一个组件地装,装一个测一次,最后发现,WebDeploy这个组件,会占用80端口. ...

  8. c语言mysql api

    原文:c语言mysql api 1.mysql_affected_rows()            //返回上次UPDATE.DELETE或INSERT查询更改/删除/插入的行数. 2.mysql_ ...

  9. Javascript实战开发:教你使用raphael.js绘制中国地图

    最近的数据统计项目中要用到中国地图,也就是在地图上动态的显示某个时间段某个省份地区的统计数据,我们不需要flash,仅仅依靠raphael.js以及SVG图像就可以完成地图的交互操作.在本文中,我给大 ...

  10. DDD分层架构之我见

    DDD分层架构之我见 前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定 ...