更多内容,前往个人博客

一、通过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. mysql剪贴板

    // mysql 8.0 连接数据源的配置文件 <!--数据源--><bean id="dataSource" class="com.alibaba.d ...

  2. HTML初体验之各种标签练习

    HTML初体验之各种标签练习 首先是<!DOCTYPE>标签 放在网页顶部的doctype声明是让浏览器进入正确呈现模式的关键.浏览器自动切换到恰当的呈现模式,以便正确显示由doctype ...

  3. 22 BootStrapModelForm

    方便之处在于,我们不会再一遍一遍的写form的样式了. from django import forms class BootStrapModelForm(forms.ModelForm): def ...

  4. 2022-07-09 第一小组 张明旭 前端HTML学习记录

    今天是正式学习的第一天,第一次接触前端内容,蒙了一圈又一圈,老师讲课速度是1000圈/s!!!!!!(插一句老师的打字和打代码速度起飞!) 好在有视频回放而且跟了笔记,能复习n遍.加油!!! 主要学习 ...

  5. vue后台管理系统——数据统计模块

    电商后台管理系统的功能--数据统计模块 1. 数据统计概述 数据统计模块主要用于统计电商平台运营过程的中的各种统计数据,并通过直观的可视化方式展示出来,方便相关运营和管理人员查看. 2. 用户来源数据 ...

  6. redis字段使用说明

    Set(集合)增删改查: #删除当前选择数据库中的所有key127.0.0.1:6379> flushdbOK#生成set集合,添加4个数据127.0.0.1:6379> sadd set ...

  7. L0范式、L1范式、L2范式解释通俗版

    L0范数是指向量中非0的元素的个数.(L0范数很难优化求解) L1范数是指向量中各个元素绝对值之和 L2范数是指向量各元素的平方和然后求平方根 L1范数可以进行特征选择,即让特征的系数变为0. L2范 ...

  8. Logtxt

    public static void SSOAuthenLog(string msg, bool isRemoteAuthen = true)        {            #region  ...

  9. Python 爬虫代码应该怎么写?

    对于入行已久的老程序员也并不一定精通爬虫代码,这些需要时间的沉淀还需要更多的实战案例,简单的问句你真的会写爬虫么?下面就是我日常写的一个y文件加上几个请求并且把需要的功能全部实现模块化,可以让我们爬虫 ...

  10. layui.dtree的学习,自定义扩展toolbar按钮(toolbarExt)

    学习layui.dtree请前往 http://www.wisdomelon.com/DTreeHelper/ 记录一下dtree的自定义扩展toolbar按钮(toolbarExt) html代码: ...