摘要:对于一个资源对象,必须先生产再消费,消费后再生产,如此循环往复。为了解决这个并发问题,提供两种解决方案,一是使用synchronized关键字和Object对象的监听器,二是使用java.util.concurrent.locks下的类Lock和Condition。

§前言

  定义一个实体类,名为资源,它有两个属性,分别是姓名和性别。兹有两个线程, 一个是生产者,负责给资源实例的姓名和性别赋值,一个是消费者,负责打印资源实例的姓名和性别的值。

  要求:有序生产和消费资源,即对于一个资源对象,必须先生产-消费-生产-消费,如此循环往复;当然了,第一个操作可能是消费者,这是没有资源可以消费的。

  解决问题:生产者对资源对象赋值后进入等待状态,并唤醒消费者输出资源属性,输出后立刻唤醒生产者。一般要加入个标志进行辅助,判断能否生产或者消费。

§案例分析

  有两个解决方案,一是使用synchronized关键字,二是使用java.util.concurrent.locks下的类Lock和Condition。两个方案都需要加入一个标志,标记当前是否允许打印或者赋值。

§使用同步锁synchronized关键字

package com.eg.wiener.conccurrency;

/**
* 妖的出现,有序打印字符串信息
*/
class Resource {
/* 姓名 */
private String name;
/* 性别 */
private String sex;
//标记一个操作单元是否完成,true 是
private boolean flag = false; //赋值功能。
public synchronized void set(String name, String sex) {
if (flag) {
try {
// 赋值函数进入等待状态
this.wait();
} catch (InterruptedException e) {
}
}
this.name = name;
this.sex = sex;
flag = true;
// 赋值结束,唤醒打印函数
this.notify();
} /**
* 消费者,获取资源属性
*/
public synchronized void out() {
if (!flag)
try {
//进入等待状态,停止打印
this.wait();
} catch (InterruptedException e) {
}
System.out.println(name + "------" + sex);
flag = false;
//打印结束,唤醒资源赋值函数
this.notify();
}
} //生产者,赋值线程任务
class Input implements Runnable {
private Resource r;
//任务一初始化就必须有要处理的资源。
Input(Resource r) {
this.r = r;
}
// 赋值
public void run() {
int x = 0;
while (true) {
if (x == 0) {
r.set("张飞", "男");
} else {
r.set("rose", "女女女女");
}
//根据取模结果实现切换
x = (x + 1) % 2;
}
}
} //获取值线程任务
class Output implements Runnable {
private Resource r; Output(Resource r) {
this.r = r;
} public void run() {
while (true) {
r.out();
}
}
} public class ThreadTest2_3 {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}

  实现方案分析:通过synchronized关键字锁定同一个资源(记住 currentRes)后,定义标记flag,由flag的值决定打印或者赋值操作是否允许,不允许的时候就调用currentRes的wait函数,让出CPU执行权,唤醒另一个操作。

§使用JUC的Lock和Condition

  在 Condition 对象中,await、signal 和 signalAll三个方法分别对应于 Object实例的三个监视器方法 wait、notify 和 notifyAll。Condition 接口与 Lock 配合实现了等待/通知模式,要为某个 Lock 对象(例如 aLock)获得 Condition 对象,需要aLock调用函数newCondition() 。

package com.eg.wiener.conccurrency;

import java.util.concurrent.locks.*;

/**
* 程序改成JDK1.5的Lock Condition接口
*/
class Resource {
private String name;
private String sex;
//定义标记
private boolean flag = false; //先创建锁对象
private final Lock lock = new ReentrantLock(); //通过锁对象获取监视器对象
private Condition con = lock.newCondition(); //赋值功能
public void set(String name, String sex) {
lock.lock();
try {
if (flag) {
try {
//     该线程将释放锁、节点加入等待队列进入等待状态
con.await();
} catch (InterruptedException e) {
}
}
this.name = name;
this.sex = sex;
flag = true;
con.signal();
} finally {
lock.unlock();
}
} //获取值
public void out() {
lock.lock();
try {
if (!flag) {
try {
con.await();
} catch (InterruptedException e) {
}
}
System.out.println(name + "------" + sex);
flag = false;
con.signal();
} finally {
lock.unlock();
}
}
} //赋值线程任务
class Input implements Runnable {
private Resource r; //任务一初始化就必须有要处理的资源
Input(Resource r) {
this.r = r;
} public void run() {
int x = 0;
while (true) {
if (x == 0) {
r.set("张飞", "男");
} else {
r.set("rose", "女女女女");
}
x = (x + 1) % 2;
}
}
} //获取值线程任务
class Output implements Runnable {
private Resource r; Output(Resource r) {
this.r = r;
} public void run() {
while (true) {
r.out();
}
}
} public class ThreadTest2_4 {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}

§小结

  文章到这里就结束了,看完之后你有什么想法想要跟大家分享呢?评论区在等着你!

§Reference

面试题:Java并发编程生产者和消费者有序消费问题的更多相关文章

  1. Java 并发编程 生产者消费者模式

    本文部分摘自<Java 并发编程的艺术> 模式概述 在线程的世界里,生产者就是生产数据的线程,消费者就是消费数据的数据.生产者和消费者彼此之间不直接通信,而是通过阻塞队列进行通信,所以生产 ...

  2. Java并发编程面试题 Top 50 整理版

    本文在 Java线程面试题 Top 50的基础上,对部分答案进行进行了整理和补充,问题答案主要来自<Java编程思想(第四版)>,<Java并发编程实战>和一些优秀的博客,当然 ...

  3. Java并发编程75道面试题及答案

    1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon( ...

  4. Java并发编程73道面试题及答案 —— 面试稳了

    今天主要整理一下 Java 并发编程在面试中的常见问题,希望对需要的读者有用. 1.在java中守护线程和本地线程区别? java中的线程分为两种:守护线程(Daemon)和用户线程(User). 任 ...

  5. 【Java并发编程】synchronized相关面试题总结

    目录 说说自己对于synchronized关键字的了解 synchronized关键字的三种使用 synchronized关键字的底层原理 JDK1.6之后对synchronized关键字进行的优化 ...

  6. 【Java并发编程】阿里最喜欢问的几道线程池的面试题?

    引言 上一篇文章我们有介绍过线程池的一个基本执行流程<[Java并发编程]面试必备之线程池>以及它的7个核心参数,以及每个参数的作用.以及如何去使用线程池 还留了几个小问题..建议看这篇文 ...

  7. 【转】关于Java并发编程的总结和思考

    一.前言 就是想学习Java并发编程了,所以转载一下这篇认为还不错的博客~ 二.正文 编写优质的并发代码是一件难度极高的事情.Java语言从第一版本开始内置了对多线程的支持,这一点在当年是非常了不起的 ...

  8. JAVA并发编程J.U.C学习总结

    前言 学习了一段时间J.U.C,打算做个小结,个人感觉总结还是非常重要,要不然总感觉知识点零零散散的. 有错误也欢迎指正,大家共同进步: 另外,转载请注明链接,写篇文章不容易啊,http://www. ...

  9. java并发编程实战学习(3)--基础构建模块

    转自:java并发编程实战 5.3阻塞队列和生产者-消费者模式 BlockingQueue阻塞队列提供可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列已经满了,那么put ...

  10. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

随机推荐

  1. 异步编程——CompletableFuture详解

    Future JDK5 新增了Future接口,用于描述一个异步计算的结果. 虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,我们必须使用Future.g ...

  2. 【自荐】一款简洁、开源的在线白板工具 Drawnix

    在线白板工具 Drawnix -- 名字源于绘画(Draw)与凤凰(Phoenix)的灵感交织. Drawnix 的定位是一个开箱即用.开源.免费的在线白板工具产品, 集思维导图.流程图.画笔于一体, ...

  3. HarmonyOS SDK让小红书鸿蒙用户尽享原生相机的拍摄之美

    小红书是深受年轻人喜爱的生活社交类社区平台,越来越多的人在小红书上分享旅行.日常.心情.近日,不少使用鸿蒙原生版小红书的细心用户已经发现,直接使用小红书拍摄照片与自己使用原相机拍摄有一样清晰美观的呈现 ...

  4. Jmeter tcp 返回500,但服务器收到请求

    解决方法:再end of line(Eol)bytes value 正确写上报文最后两位十进制字节码

  5. 数据库MVCC详解

    MVCC 1.基本介绍 数据库:MySQL.[很多主流数据库都使用了MVCC,比如MySQL的InnoDB引擎.PostgreSQL.Oracle] MVCC,全称Multi-Version Conc ...

  6. 【调研】Vision Language Model Safety

    Adversarial Attacks White-box Attacks Task-specific Attacks 的目标是针对某个具体的任务(如图像描述生成.指代表达理解等),通过精心设计的对抗 ...

  7. 【Linux】3.8 Linux磁盘分区、挂载

    Linux磁盘分区.挂载 1. 分区方式 mbr分区 最多支持四个主分区 系统只能安装在主分区 扩展分区要占一个主分区 MBR最大只支持2TB,但拥有最好的兼容性 gpt分区 支持无限多个主分区(但操 ...

  8. CompletableFuture原理及应用场景详解

    1.应用场景 现在我们打开各个APP上的一个页面,可能就需要涉及后端几十个服务的API调用,比如某宝.某个外卖APP上,下面是某个外卖APP的首页.首页上的页面展示会关联很多服务的API调用,如果使用 ...

  9. 3. RabbitMQ 的(Hello World) 和 RabbitMQ 的(Work Queues)工作队列

    3. RabbitMQ 的(Hello World) 和 RabbitMQ 的(Work Queues)工作队列 @ 目录 3. RabbitMQ 的(Hello World) 和 RabbitMQ ...

  10. AndrodStudio构建时报错Could not find com.android.tools.build:gradle:xxx

    检查配置的版本号在maven仓库里有没有. maven仓库地址: https://repo1.maven.org/maven2/com/android/tools/build/gradle/ 选择需要 ...