概要

本章,会对“生产/消费者问题”进行讨论。涉及到的内容包括:
1. 生产/消费者模型
2. 生产/消费者实现

转载请注明出处:http://www.cnblogs.com/skywang12345/p/3480016.html

1. 生产/消费者模型

生产/消费者问题是个非常典型的多线程问题,涉及到的对象包括“生产者”、“消费者”、“仓库”和“产品”。他们之间的关系如下:
(01) 生产者仅仅在仓储未满时候生产,仓满则停止生产。
(02) 消费者仅仅在仓储有产品时候才能消费,仓空则等待。
(03) 当消费者发现仓储没产品可消费时候会通知生产者生产。
(04) 生产者在生产出可消费产品时候,应该通知等待的消费者去消费。

2. 生产/消费者实现

下面通过wait()/notify()方式实现该模型(后面在学习了线程池相关内容之后,再通过其它方式实现生产/消费者模型)。源码如下:

  1 // Demo1.java2 // 仓库3 class Depot {4     private int capacity;    // 仓库的容量5     private int size;        // 仓库的实际数量6 7     public Depot(int capacity) {8         this.capacity = capacity;9         this.size = 0;10     }11 12     public synchronized void produce(int val) {13         try {14              // left 表示“想要生产的数量”(有可能生产量太多,需多此生产)15             int left = val;16             while (left > 0) {17                 // 库存已满时,等待“消费者”消费产品。18                 while (size >= capacity)19                     wait();20                 // 获取“实际生产的数量”(即库存中新增的数量)21                 // 如果“库存”+“想要生产的数量”>“总的容量”,则“实际增量”=“总的容量”-“当前容量”。(此时填满仓库)22                 // 否则“实际增量”=“想要生产的数量”23                 int inc = (size+left)>capacity ? (capacity-size) : left;24                 size += inc;25                 left -= inc;26                 System.out.printf("%s produce(%3d) --> left=%3d, inc=%3d, size=%3d\n", 27                         Thread.currentThread().getName(), val, left, inc, size);28                 // 通知“消费者”可以消费了。29                 notifyAll();30             }31         } catch (InterruptedException e) {32         }33     } 34 35     public synchronized void consume(int val) {36         try {37             // left 表示“客户要消费数量”(有可能消费量太大,库存不够,需多此消费)38             int left = val;39             while (left > 0) {40                 // 库存为0时,等待“生产者”生产产品。41                 while (size <= 0)42                     wait();43                 // 获取“实际消费的数量”(即库存中实际减少的数量)44                 // 如果“库存”<“客户要消费的数量”,则“实际消费量”=“库存”;45                 // 否则,“实际消费量”=“客户要消费的数量”。46                 int dec = (size<left) ? size : left;47                 size -= dec;48                 left -= dec;49                 System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n", 50                         Thread.currentThread().getName(), val, left, dec, size);51                 notifyAll();52             }53         } catch (InterruptedException e) {54         }55     }56 57     public String toString() {58         return "capacity:"+capacity+", actual size:"+size;59     }60 } 61 62 // 生产者63 class Producer {64     private Depot depot;65     66     public Producer(Depot depot) {67         this.depot = depot;68     }69 70     // 消费产品:新建一个线程向仓库中生产产品。71     public void produce(final int val) {72         new Thread() {73             public void run() {74                 depot.produce(val);75             }76         }.start();77     }78 }79 80 // 消费者81 class Customer {82     private Depot depot;83     84     public Customer(Depot depot) {85         this.depot = depot;86     }87 88     // 消费产品:新建一个线程从仓库中消费产品。89     public void consume(final int val) {90         new Thread() {91             public void run() {92                 depot.consume(val);93             }94         }.start();95     }96 }97 98 public class Demo1 {  99     public static void main(String[] args) {
100         Depot mDepot = new Depot(100);
101         Producer mPro = new Producer(mDepot);
102         Customer mCus = new Customer(mDepot);
103
104         mPro.produce(60);
105         mPro.produce(120);
106         mCus.consume(90);
107         mCus.consume(150);
108         mPro.produce(110);
109     }
110 }

说明
(01) Producer是“生产者”类,它与“仓库(depot)”关联。当调用“生产者”的produce()方法时,它会新建一个线程并向“仓库”中生产产品。
(02) Customer是“消费者”类,它与“仓库(depot)”关联。当调用“消费者”的consume()方法时,它会新建一个线程并消费“仓库”中的产品。
(03) Depot是“仓库”类,仓库中记录“仓库的容量(capacity)”以及“仓库中当前产品数目(size)”。
        “仓库”类的生产方法produce()和消费方法consume()方法都是synchronized方法,进入synchronized方法体,意味着这个线程获取到了该“仓库”对象的同步锁。这也就是说,同一时间,生产者和消费者线程只能有一个能运行。通过同步锁,实现了对“残酷”的互斥访问。
       对于生产方法produce()而言:当仓库满时,生产者线程等待,需要等待消费者消费产品之后,生产线程才能生产;生产者线程生产完产品之后,会通过notifyAll()唤醒同步锁上的所有线程,包括“消费者线程”,即我们所说的“通知消费者进行消费”。
      对于消费方法consume()而言:当仓库为空时,消费者线程等待,需要等待生产者生产产品之后,消费者线程才能消费;消费者线程消费完产品之后,会通过notifyAll()唤醒同步锁上的所有线程,包括“生产者线程”,即我们所说的“通知生产者进行生产”。

(某一次)运行结果

Thread-0 produce( 60) --> left=  0, inc= 60, size= 60
Thread-4 produce(110) --> left= 70, inc= 40, size=100
Thread-2 consume( 90) <-- left=  0, dec= 90, size= 10
Thread-3 consume(150) <-- left=140, dec= 10, size=  0
Thread-1 produce(120) --> left= 20, inc=100, size=100
Thread-3 consume(150) <-- left= 40, dec=100, size=  0
Thread-4 produce(110) --> left=  0, inc= 70, size= 70
Thread-3 consume(150) <-- left=  0, dec= 40, size= 30
Thread-1 produce(120) --> left=  0, inc= 20, size= 50

java多线程系类:基础篇:10生产者消费者的问题相关推荐

  1. java多线程系类:JUC原子类:04之AtomicReference原子类

    概要 本章对AtomicReference引用类型的原子类进行介绍.内容包括: AtomicReference介绍和函数列表 AtomicReference源码分析(基于JDK1.7.0_40) At ...

  2. Java程序设计语言(基础篇)机械工业出版社 原属第10版

    链接:https://pan.baidu.com/s/1txoB3sk0SdT1u8RpS2x-Kg  提取码:rxkd  Java程序设计语言(基础篇)机械工业出版社 原属第10版 外加一本Java ...

  3. JAVA多线程和并发基础面试问答(转载)

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

  4. Java学习---Day16_IO流基础篇

    Java学习-Day16_IO流基础篇 文件操作 操作磁盘上的某一个文件或某一个文件夹,可以对他们进行创建或删除.移动.属性获取.属性设置等操作.但是,不包含读取文件的内容.拷贝文件 ps:java中 ...

  5. Java学习笔记之基础篇

    Java学习笔记之基础篇 目录 Java如何体现平台的无关性? 面向对象(OO)的理解 面向对象和面向过程编程的区别 面向对象三大特征 静态绑定和动态绑定(后期绑定) 延伸:类之间的关系 组合(聚合) ...

  6. invader的java学习第二天基础篇

    invader的java学习第二天基础篇 一.深入了解第一个Hello World程序 public class Hello World{ public static void main(String ...

  7. JAVA学习之路--基础篇三

    目录 关于Java中从键盘输入的语句 nextxxx().next().nextLine()的区别 语句 if和if else语句 Switch语句 for语句 while和do..while bre ...

  8. [转载] JAVA语言程序设计(基础篇)第十版课后题答案(第一章)

    参考链接: Java中的Scanner和nextChar() JAVA语言程序设计(基础篇)第十版课后题答案 第一章 第二题 /** Created by ysy on 2018/7/6. */ pu ...

  9. java语言程序设计基础篇课后答案_《Java语言程序设计:基础篇》课后复习题答案-第十五章.pdf...

    <Java语言程序设计:基础篇>课后复习题答案-第十五章 Chapter15Graphics 1. Theycoordinateshouldincreaseandthexcoordinat ...

最新文章

  1. php回顶层顶层,PHP语言之php顶层类(自家用)
  2. java float 加法_JAVA 实现精确的加减乘除运算
  3. 实验吧— Crypto Write up(一)
  4. mysql练习_创建库与列表、增加列表信息、列表查询(包含多列表查询)_月隐学python第23课
  5. 迁移 WinForm 应用从 dotnet framework 到 dotnetcore3.0
  6. 设计精美Power Bi报告的诀窍以及让人眼前一亮的精美Power Bi图表
  7. docker查找镜像_5 款非常好用的开源 Docker 工具,get一波~
  8. SQL:如何用一个sql统计出全校男生个数、女生个数以及总人数
  9. mysql 搜索正则表达式_mysql必知必会--用正则表达式 进行搜索
  10. php打开文件对话框,JS打开选择本地文件的对话框
  11. VScode 搭建 django 开发环境(Win Python3.71 django1.11.11)
  12. 牛客小白月赛6 A 鲲
  13. 高德地图 web API 多点路线绘制
  14. php查重,知网查重时检测php之类的源码吗?【干货分享】
  15. 硬件PCB发热严重,天线信号不良的整改
  16. dimm和udimm_RDIMM和UDIMM内存的区别
  17. ResizeObserver loop completed with undelivered notifications.
  18. watch中的深度监听
  19. 374. Guess Number Higher or Lower*
  20. windows android 对比度,色彩对比度饱和度基本测试

热门文章

  1. 数据结构7.5_有向无环图及其应用
  2. 理解 IEnumerable 与 IEnumerator
  3. python3—廖雪峰之练习(三)
  4. LeetCode 5.Longest Palindromic Substring 求解
  5. 灵玖软件大数据采集技术提高出版行业效率
  6. Amoeba实现mysql主从读写分离
  7. 100c之37:爱因斯坦问题
  8. wordpress主题wp-office2010初版
  9. 整合企业监控孤岛 Mocha BSM走进香港物流行业——香港空运货站有限公司
  10. 趣图:好好干,今天再加个班