一个古典的例子关于一个生产者线程和一个消费者线程之间的通信。生产者线程产生数据条目供消费者线程消费。每一个生产数据条目都存储在一个共享的变量。

想像一下,线程运行在存在不同的速度。消费者接受先前进程的数据条目之前,生产者可能生产一个新的数据条目和记录在共享的变量中。也可以是,一个新的数据条目产生之前,消费者可能接受共享变量的内容。

为了去解决这个问题,生产者线程必须等待直到先前产生的数据条目已经被消费,它才可以被通知唤醒;和一个消费者线程必须等待直到一个新的数据条目被生产,它才可以被通知唤醒。Listing3-1展示了如何去完成这个任务通过wait()和notify()方法。

package com.owen.thread.chapter3;public class PC
{public static void main(String[] args){Shared s = new Shared();new Producer(s).start();new Consumer(s).start();}
}class Shared
{private char c;private volatile boolean writeable = true;synchronized void setSharedChar(char c){while (!writeable)try{wait();} catch (InterruptedException ie){}this.c = c;writeable = false;notify();}synchronized char getSharedChar(){while (writeable)try{wait();} catch (InterruptedException ie){}writeable = true;notify();return c;}
}class Producer extends Thread
{private final Shared s;Producer(Shared s){this.s = s;}@Overridepublic void run(){for (char ch = 'A'; ch <= 'Z'; ch++){s.setSharedChar(ch);System.out.println(ch + " produced by producer.");}}
}class Consumer extends Thread
{private final Shared s;Consumer(Shared s){this.s = s;}@Overridepublic void run(){char ch;do{ch = s.getSharedChar();System.out.println(ch + " consumed by consumer.");} while (ch != 'Z');}
}

这个应用创建一个Share对象和两个线程,两个线程获取一个对象引用的副本。这个生产者请求对象setShareChar()方法去保存26个字母其中一个;消费者请求对象getShareChar()方法去获取每一个字母。

writeable实例域有两种状态:生产者等待消费者去消费数据,和消费者等待生产者去产生新的数据。这个使生产者和消费者之间有序地进行。下面的方案,这里首先消费者先执行,说明这个有序性:

(1)这个消费者执行s.getShareChar()去接收一个字母。

(2) 在同步方法内部,消费者请求wait()方法,因为writeable保持true。消费者一直在等待直到它从生产者那里得到通知。

(3)生产者最终执行s.setShareChar(ch);

(4)当生产者进入到同步方法(这是可能的,因为消费者释放锁在wait()内部方法优先于等待),生产者重新发现writeable的值是true和不能请求wait().

(5) 生产者保存字母,让writeable为false(这个将导致生产在下一次setShareChar()请求时处于等待,如果消费者不能消费这个字母,那么将会一直处于等待)和请求notify()去唤醒消费者(假设消费者在等待)。

(6)生产者存在setShareChar(char c).

(7) 消费者唤醒(和再次请求锁),让writeable为true(这会导致消费者在下一次getShareChar()请求时处于等待,当生产者不能生产字母,那它将一直处于等待),通知生产者去唤醒这个线程(假设生产者在等待),和返回一个shared。

执行上面的代码,你可能会得到如下的结果:

W produced by producer.
W consumed by consumer.
X produced by producer.
Y produced by producer.
X consumed by consumer.
Y consumed by consumer.
Z produced by producer.
Z consumed by consumer.

尽管同步工作是正确的,你可能在多消费的信息之前你会发现多个输出信息。

A produced by producer.
B produced by producer.
A consumed by consumer.
B consumed by consumer.

你也可能在生产的信息之前发现消费信息输出。

V produced by producer.
V consumed by consumer.

这些信息的输出不是意味着生产者和消费者的线程不是同步。相反,请求setShareChar()方法返回的结果是通过应用的System.out.println()方法请求的,而这个方法是不同步的,和请求getShareChar()方法是通过应用的System.out.prinltn()方法请求的,而这个方法是不同步的。通过擦除每个这样的方法,加上同步锁在Share对象引用的s,那么输出的顺序就会是正确的,如Listing3-2的例子所示:

package com.owen.thread.chapter3;public class PC3_2
{public static void main(String[] args){Shared3_2 s = new Shared3_2();new Producer3_2(s).start();new Consumer3_2(s).start();}
}class Shared3_2
{private char c;private volatile boolean writeable = true;synchronized void setSharedChar(char c){while (!writeable)try{wait();} catch (InterruptedException ie){}this.c = c;writeable = false;notify();}synchronized char getSharedChar(){while (writeable)try{wait();} catch (InterruptedException ie){}writeable = true;notify();return c;}
}class Producer3_2 extends Thread
{private final Shared3_2 s;Producer3_2(Shared3_2 s){this.s = s;}@Overridepublic void run(){for (char ch = 'A'; ch <= 'Z'; ch++){synchronized (s){s.setSharedChar(ch);System.out.println(ch + " produced by producer.");}}}
}class Consumer3_2 extends Thread
{private final Shared3_2 s;Consumer3_2(Shared3_2 s){this.s = s;}@Overridepublic void run(){char ch;do{synchronized (s){ch = s.getSharedChar();System.out.println(ch + " consumed by consumer.");}} while (ch != 'Z');}

执行例子输出信息如下:

A produced by producer.
A consumed by consumer.
B produced by producer.
B consumed by consumer.
C produced by producer.
C consumed by consumer.
D produced by producer.
D consumed by consumer.

源码下载: Git @github.com:owenwilliam/Thread. git

3.2生产者和消费者(Producers and Consumers)相关推荐

  1. java 线程同步的list_java线程生产者与消费者实例(使用List实现同步)

    过多的线程同步可能会造成死锁 死锁通俗来讲就是两个或者两个以上线程,占用了对方下一步所需要的资源,多个线程僵持都无法结束任务的状态 生产者和消费者模式是一个多线程同步的经典案例 它利用信号灯来判断线程 ...

  2. kafka中生产者和消费者的分区问题

    本文来书说下kafka中生产者和消费者的分区问题 文章目录 概述 主题的分区数设置 分区与生产者 分区与消费者 range roundrobin(轮询) 本文参考 本文小结 概述 我们知道,生产者发送 ...

  3. Linux操作系统实验:生产者和消费者问题

    一.实验目的及要求 "生产者消费者"问题是一个著名的同时性编程问题的集合.通过编写经典的"生产者消费者"问题的实验,读者可以进一步熟悉 Linux 中多线程编程 ...

  4. JAVA并发编程 之 LMAX Disruptor使用实例(高效解决生产者与消费者问题)

    什么是Disruptor? Disruptor是一个开源的JAVA框架,它被设计用于在生产者-消费者(producer-consumer problem,简称PCP)问题上获得尽量高的吞吐量(TPS) ...

  5. RabbitMQ 入门系列(2)— 生产者、消费者、信道、代理、队列、交换器、路由键、绑定、交换器

    本系列是「RabbitMQ实战:高效部署分布式消息队列」和 「RabbitMQ实战指南」书籍的读书笔记. RabbitMQ 中重要概念 1. 生产者 生产者(producer)创建消息,然后发送到代理 ...

  6. linux进程间通信:system V 信号量 生产者和消费者模型编程案例

    生产者和消费者模型: 有若干个缓冲区,生产者不断向里填数据,消费者不断从中取数据 两者不冲突的前提: 缓冲区有若干个,且是固定大小,生产者和消费者各有若干个 生产者向缓冲区中填数据前需要判断缓冲区是否 ...

  7. Python中的生产者与消费者模式(转载)

    利用多线程和队列可以实现生产者消费者模式.该模式通过平衡生产线程和消费线程的工作能力来提高程序整体处理数据的速度. 1.什么是生产者和消费者? 在线程世界里,生产者就是生产数据(或者说发布任务)的线程 ...

  8. 12.多线程的实现方式、线程安全问题的产生与解决以及生产者与消费者问题

    一.实现多线程 1.1 了解多线程 多线程是指从软件或者硬件上实现多个线程并发执行的技术,具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,从而提升性能. 1.2 并发与并行 并行是指同 ...

  9. java多线程 生产者消费者_java多线程之-生产者与消费者

    java多线程之-并发协作[生产者与消费者]模型 对于多线程程序来说,不管c/c++ java python 等任何编程语言,生产者与消费者模型都是最为经典的.也就是可以说多线程的并发协作 对于此模型 ...

  10. python生产和消费模型_python queue和生产者和消费者模型

    queue队列 当必须安全地在多个线程之间交换信息时,队列在线程编程中特别有用. classqueue.Queue(maxsize=0) #先入先出classqueue.LifoQueue(maxsi ...

最新文章

  1. docker 命令详解(cp篇)
  2. 牛客小白月赛2-B小马过河(求点到直线的垂足)
  3. ~~~~我是一棵小树苗~~~~
  4. Djaongo 中间件
  5. 慧材技术中核普达测量与测绘系统介绍(一)
  6. 【STL源码剖析读书笔记】【第6章】算法之partition算法
  7. java发送http post请求报文_Java 用HTTP的方式发送JSON报文请求
  8. 暑假集训-7.31总结
  9. 张宇1000题高等数学 第九章 一元函数积分学的计算
  10. ubuntu清理磁盘空间的几个技巧
  11. 例2.2 圆柱体的表面积
  12. html自动拿微信扫描,HTML——微信浏览器H5页面调用微信扫一扫
  13. 【Lorenz混沌】基于FPGA的Lorenz混沌系统verilog实现
  14. postgreSQL安装成功后打开pgadmin4出现错误:Fatal error:The pgAdmin 4 server could not be contacted:
  15. Python图像处理笔记——形态学处理(skimage.morphology)
  16. C语言求ax2+bx+c=0的解,解一元二次方程
  17. 【原创】从BZOJ2683 简单题中 整 CDQ分治解决三维偏序
  18. Error: docker-ce conflicts with 2:docker-1.13.1-209.git7d71120.el7.centos.x86_64
  19. DM8 roll.dbf损坏修复
  20. 最新卡通星空酷炫PPT模板

热门文章

  1. 51nod 1105 第K大的数 【双重二分/二分套二分/两数组任意乘积后第K大数】
  2. Android中Service的一个Demo例子
  3. 在CLR中自动本地化正在运行的.NET窗口
  4. cp 时间长 linux,为了节省cp命令时间,结果换来了重装linux系统的差事
  5. DHCP offer报文到底是单播还是广播?
  6. nyoj--2--括号配对
  7. QLV格式的视频怎么在线转化成MP4
  8. linux/unix 上那些炫酷的命令行工具(一)
  9. js 浅拷贝和深拷贝
  10. 移动端1px细线问题