更多内容,前往个人博客

一、通过synchronize 中的 wait 和 notify 实现


【1】我们可以将生产者和消费者需要的方法写在公共类中

 1 package com.yintong.concurrent;
2
3 import java.util.LinkedList;
4
5 public class Concurrentcomm {
6 //常量
7 private static int MAX_VALUE = 10;
8 //可以理解为缓存
9 LinkedList<String> linkedList = new LinkedList<>();
10 Object object = new Object();
11 /*
12 * 生产者方法
13 */
14 public void product() throws Exception {
15 synchronized(linkedList) {
16 while(MAX_VALUE == linkedList.size()) {
17 System.out.println("仓库已满,【生产者】: 暂时不能执行生产任务!");
18 linkedList.wait();
19 }
20 linkedList.push(" 李四 ");
21 System.out.println("【生产者】:生产了一个产品\t【现仓储量为】:" + linkedList.size());
22 linkedList.notifyAll();
23 }
24 }
25 /*
26 * 消费者方法
27 */
28 public void customer() throws Exception {
29 /*
30 * 根据jdk的void notifyAll()的描述,“解除那些在该对象上调用wait()方法的线程的阻塞状态。该方法只能在同步方法或同步块内部调用。
31 * 如果当前线程不是对象所得持有者,
32 * 该方法抛出一个java.lang.IllegalMonitorStateException 异常”
33 * so我们使用同一把锁
34 */
35 synchronized (linkedList) {
36 //多线程判断中使用 while 不要使用 if 否则会出现虚假唤醒问题
37 while(linkedList.size() == 0) {
38 System.out.println("仓库无货,【消费者】: 暂时不能执行消费任务!");
39 linkedList.wait();
40 }
41 linkedList.pop();
42 System.out.println("【消费者】:消费了一个产品\t【现仓储量为】:" + linkedList.size());
43 linkedList.notifyAll();
44 }
45 }
46 }

【2】在 main 函数中调用生产者和消费者方法,并加限制即可

 1 /**
2 * @author zzx
3 * @desc 生产者与消费者
4 *
5 */
6 public class Concurrent {
7 //常量
8 private static int MAX_VALUE = 100;
9
10 public static void main(String[] args) {
11 Concurrentcomm con = new Concurrentcomm();
12 new Thread(new Runnable() {
13
14 @Override
15 public void run() {
16 try {
17 for (int i = 0; i < MAX_VALUE; i++) {
18 Thread.sleep(0);
19 con.product();
20 }
21 } catch (Exception e) {
22 // TODO Auto-generated catch block
23 e.printStackTrace();
24 }
25 }
26 }).start();
27 // 消费者
28 new Thread(new Runnable() {
29
30 @Override
31 public void run() {
32 try {
33 Thread.sleep(10);
34 for (int i = 0; i < MAX_VALUE; i++) {
35 con.customer();
36 }
37 } catch (Exception e) {
38 e.printStackTrace();
39 }
40 }
41 }).start();
42 }
43 }

【3】简单的生产者与消费者模式就完成了,可以看下运行的结果

二、通过 Lock 中的 await 与 signalAll 实现


【1】我们将公共的属性和方法放在 Resouce 类中,在资源类中使用 Lock 中的 lock()进行加锁,控制并发操作。使用 await()方法阻塞线程。使用 signalAll()唤醒线程。

 1 /**
2 * 通过 Lock 实现生产者与消费者
3 * 资源类:将公共的资源放在一个单独的类中,可以将其看做一个产品,自身就就有生产和消费的能力(方法)
4 */
5 public class ProductAndConsumer {
6 public static void main(String[] args) {
7 Resouce resouce = new Resouce();
8 //生产者
9 new Thread(()->{
10 for (int i=1;i<=5;i++) {
11 resouce.product();
12 }
13 },String.valueOf("生产者")) .start();
14
15 //消费者
16 new Thread(()->{
17 for (int i=1;i<=5;i++){
18 resouce.consumer();
19 }
20 },String.valueOf("消费者")).start();
21 }
22 }
23 //资源类
24 class Resouce {
25 private int MAX_VALUE = 3;
26 private int MIN_VALUE = 0;
27 private int number = 0;
28 private Lock lock = new ReentrantLock();
29 private Condition condition = lock.newCondition();
30
31 //生产者
32 public void product(){
33 try {
34 lock.lock();
35 //如果生产的数量大于最大值则阻塞
36 while(number >= MAX_VALUE){
37 condition.await();
38 }
39 number++;
40 System.out.println("【生产者】:生产了一个产品\t【现仓储量为】:" + number);
41 condition.signalAll();
42 } catch (InterruptedException e) {
43 e.printStackTrace();
44 }finally {
45 lock.unlock();
46 }
47 }
48
49 //消费者
50 public void consumer(){
51 try {
52 lock.lock();
53 //如果消费的值=0则阻塞
54 while(number <= MIN_VALUE){
55 condition.await();
56 }
57 number--;
58 System.out.println("【消费者】:消费了一个产品\t【现仓储量为】:" + number);
59 condition.signalAll();
60 } catch (InterruptedException e) {
61 e.printStackTrace();
62 }finally {
63 lock.unlock();
64 }
65 }
66 }

【2】输出结果展示:
 

三、synchronized 和 Lock 的区别


【1】原始构成:synchronized 是关键字属于 JVM 层面。底层通过 monitorenter(进入)monitorexit(退出)实现。底层是通过 monitor 对象完成,其实 wait/notify 等方法也依赖于 monitor 对象,只有在同步块或方法中才能调用 wait/notify 等方法。Lock 是具体类(java.util.concurrent.locks.Lock)是 API 层面的锁。
【2】使用方法:synchronized 不需要用户手动释放锁,当 synchronized 代码执行完后,系统会自动释放锁。ReentrantLock 则需要用户手动释放锁,若未主动释放锁,就可能导致出现死锁的现象。
【3】等待是否中断:synchronized 不可中断,除非抛出异常或者正常运行完成。ReentrantLock 可中断,1)、设置超时时间 tryLock(long timeout,TimeUnit unit) 2)、lockInterruptibly() 放在代码块中,调用 interrupt() 方法可中断。
【4】加锁是否公平:synchronized 非公平锁。ReentrantLock 两者都可以,默认是非公平锁,构造方法可以传入 boolean 值,true 为公平锁,false 为非公平锁。
【5】锁绑定多个条件 Condition:synchronized 没有。ReentrantLock 用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像 synchronized 要么随机唤醒一个线程要么唤醒全部线程。

四、通过阻塞队列实现生产者与消费者


【1】通过blockQueue 中的 put/take 方法实现生产者与消费者,具体实现如下:当生产者使用put 生产到指定的队列大小3时,就会阻塞当前线程。这是消费者线程会通过 take 方法消费队列中的消息。当队列中没有消息时,会阻塞,直到有消息消费。

 1 public class BlockProductConsumer {
2 public static void main(String[] args) {
3 MyResouce resouce = new MyResouce(new ArrayBlockingQueue(3));
4 //生产者线程
5 new Thread(()->{
6 for(int i=1;i<=10;i++){
7 resouce.product();
8 }
9 },"生产者").start();
10
11 //消费者线程
12 new Thread(()->{
13 for(int i=1;i<=10;i++){
14 try {
15 resouce.consumer();
16 } catch (InterruptedException e) {
17 e.printStackTrace();
18 }
19 }
20 },"消费者").start();
21
22 try {
23 TimeUnit.SECONDS.sleep(1);
24 resouce.stop();
25 } catch (InterruptedException e) {
26 e.printStackTrace();
27 }
28 }
29 }
30
31
32 /**
33 * 公共资源类
34 */
35 class MyResouce{
36 //标记 while 无限循环
37 private volatile boolean FLAG = true;
38 //队列中存入的数值
39 private AtomicInteger atomicInteger = new AtomicInteger();
40 //组合一个阻塞队列,通过构造器传入
41 private BlockingQueue blockingQueue;
42 public MyResouce(BlockingQueue blockingQueue) {
43 this.blockingQueue = blockingQueue;
44 }
45
46 //生产者
47 public void product(){
48 try {
49 while (FLAG){
50 blockingQueue.put(String.valueOf(atomicInteger.incrementAndGet()));
51 System.out.println("生产者生产第"+blockingQueue.size()+"个产品");
52 }
53 } catch (InterruptedException e) {
54 e.printStackTrace();
55 }
56 }
57
58 //消费者
59 public void consumer() throws InterruptedException {
60 while (FLAG){
61 blockingQueue.take();
62 System.out.println("消费者消费第"+(blockingQueue.size()+1)+"个产品");
63 }
64 }
65
66 public void stop(){
67 FLAG = false;
68 System.out.println("========================");
69 }
70 }

【2】效果展示:
 

Java面试——写一个生产者与消费者的更多相关文章

  1. 教你如何使用Java手写一个基于链表的队列

    在上一篇博客[教你如何使用Java手写一个基于数组的队列]中已经介绍了队列,以及Java语言中对队列的实现,对队列不是很了解的可以我上一篇文章.那么,现在就直接进入主题吧. 这篇博客主要讲解的是如何使 ...

  2. 浅谈Java简单实现的生产者与消费者问题

    一.面对生产者和消费者的问题,首先我们得明白几点: 生产者:生产数据:消费者:消费数据.消费者在没有数据可供消费的情况下,不能消费:生产者在原数据没有被消费掉的情况下,不能生产新数据.假设,数据空间只 ...

  3. java nio 写一个完整的http服务器 支持文件上传 chunk传输 gzip 压缩 使用过程 和servlet差不多

    java nio 写一个完整的http服务器  支持文件上传   chunk传输    gzip 压缩      也仿照着 netty处理了NIO的空轮询BUG        本项目并不复杂 代码不多 ...

  4. 用Java写一个生产者-消费者队列

    生产者消费者的模型作用 通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用. 解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系 ...

  5. java多线程中的生产者与消费者之等待唤醒机制@Version1.0

    一.生产者消费者模式的学生类成员变量生产与消费demo,第一版1.等待唤醒:    Object类中提供了三个方法:    wait():等待    notify():唤醒单个线程    notify ...

  6. Java:多线程之生产者与消费者

    要求:用两个线程模拟存票.售票过程.但要求每存入一张票,就售出一张票,售出后,再存入,直到售完为止. 用到的知识点:线程等待.唤醒.可能的线程中断异常 下面的方式一和方式二采用的是唤醒所有等待的线程, ...

  7. Java并发编程(4)--生产者与消费者模式介绍

    一.前言 这种模式在生活是最常见的,那么它的场景是什么样的呢? 下面是我假象的,假设有一个仓库,仓库有一个生产者和一个消费者,消费者过来消费的时候会检测仓库中是否有库存,如果没有了则等待生产,如果有就 ...

  8. Java 线程的通讯--生产者和消费者

    package 生产者和消费者; //消费者 public class Customer implements Runnable { private Share_resources rescource ...

  9. Java多线程设计模式(2)生产者与消费者模式

    1 Producer-Consumer Pattern Producer-Consumer Pattern主要就是在生产者与消费者之间建立一个“桥梁参与者”,用来解决生产者线程与消费者线程之间速度的不 ...

  10. Java实现PV操作 | 生产者与消费者

    导语 在学习操作系统的过程中,PV操作是很重要的一个环节.然而面对书本上枯燥的代码,每一个爱好技术的人总是想能亲自去实现.现在我要推出一个专题,专门讲述如何用Java实现PV操作,让操作系统背后的逻辑 ...

随机推荐

  1. c++基础技能练习详解(回文数,字符串计数,指针比大小,递归求和,类的应用)

    //1. 调用函数实现判定用户输入的正整数是否为"回文数",所谓"回文数"是指正读反读都相同的数. (25分) bool hw(string s) { for ...

  2. Linux 第三节(重定向符,通配符,管道符,转义符,VIM编辑器)

    1.输入重定向符 < 2.输出重定向符 将我们的命令原本要输出到屏幕的内容,输出到文件里面 标准信息 >  覆盖>  追加>> 错误信息 2>  覆盖2>  ...

  3. spring的作用

    Spring能有效地组织你的中间层对象,无论你是否选择使用了EJB.如果你仅仅使用了Struts或其他的包含了J2EE特有API的framework,你会发现Spring关注了遗留下的问题.Sprin ...

  4. postman收藏 -大佬玩法。

    请求顺序:    https://www.cnblogs.com/superhin/p/11454832.html 在Postman脚本中发送请求(pm.sendRequest) : https:// ...

  5. Hackintool查看CFG锁显示空白

    Hackintool是黑苹果配置的得力工具,通过在Hackintool > 工具 > 从AppleIntelInfo中获取 可以看到cfg是否成功解锁.但是如果点击该按钮后输入密码执行CP ...

  6. 系统U盘安装Windows无法打开install.wim的问题

    我们在使用UltraISO工具制作Windows操作系统安装U盘,使用U盘启动,在安装Windows操作系统的过程中,出现类似"Windows无法打开所需的文件X:\sources\inst ...

  7. MySQL事务MVCC、undolog和redolog

    MySql的MVCC多版本控制 undolog:回滚日志(保证一致性)只有在ReadCommited和RepeatableRead隔离级别有用 redolog:重写日志(保证持久性) 示例讲解 Rea ...

  8. uart 理解

    1: 串口字符串传输和单字节传输的差异体现在字节与字节间是或在停止位后有空闲位的插入(下图红箭头处,没有插入),即 uart_send("123")  和 uart_send('1 ...

  9. 使用springboot+MybatisPlus+vue实现论文基础管理系统

    页面效果如下 分析所需内容 数据库信息如下 t_paper t_comment 好了 数据库搞定 新建Springboot项目 选择Spring Initializr pom文件增加引入下面这三个依赖 ...

  10. 微信点击链接:debugx5.qq.com提示您使用的不是x5内核

    微信点击链接:debugx5.qq.com提示您使用的不是x5内核 因为要测试小程序,需要webview调试功能,正常来说在微信任意一个聊天窗口,输入:debugx5.qq.com,点击该链接就可以, ...