实践项目:生产者与消费者【经典多线程问题】

问题引出:

  生产者和消费者指的是两个不同的线程类对象,操作同一个空间资源的情况。

需求引出:

  —— 生产者负责生产数据,消费者负责取走数据

  —— 生产者生产完一组数据之后,消费者就要取走一组数据

设置三个类:数据类、生产类、消费类;生产和消费类是线程类,同时操作同一个数据类;生产类负责每次向数据类中写入一组数据;消费类负责每次从数据类中取出一组数据。

 package hello;

 class Info {  // 数据类
private String title ;
private String content;
public void setTitle(String title) {
this.title = title ;
}
public String getTitle() {
return title ;
}
public String getContent() {
return content ;
}
public void setContent(String content) {
this.content = content ;
} } class Producer implements Runnable { // 生成者类(线程)
private Info info ;
public Producer(Info info) {
this.info = info ;
}
@Override
public void run() {
for (int x = 0 ; x < 100 ; x ++ ) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
if ( x % 2 == 0 ) {
this.info.setTitle("张三");
this.info.setContent("男");
} else {
this.info.setTitle("王五");
this.info.setContent("男");
}
}
}
} class Consumer implements Runnable {
private Info info ;
public Consumer(Info info) {
this.info = info ;
}
@Override
public void run() {
for (int x = 0 ; x < 100 ; x ++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.info.getTitle() + "——" + this.info.getContent());
}
}
} public class TestDemo {
public static void main(String[] args) throws Exception {
Info info = new Info() ;
new Thread(new Producer(info)).start();
new Thread(new Consumer(info)).start();
}
}

上例程序执行后,会发现“错位的现象”;出现类似数据为取走,就存入新的数据的错误。【不同步且异步现象导致】

 package hello;

 class Info {  // 数据类
private String title ;
private String content;
public synchronized void set(String title , String content) {
this.title = title ;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} this.content = content ;
}
public synchronized void get() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.title + "——" + this.content);
} } class Producer implements Runnable { // 生成者类(线程)
private Info info ;
public Producer(Info info) {
this.info = info ;
}
@Override
public void run() {
for (int x = 0 ; x < 100 ; x ++ ) {
if ( x % 2 == 0 ) {
this.info.set("张三", "男");
} else {
this.info.set("李悦", "女");
}
}
}
} class Consumer implements Runnable {
private Info info ;
public Consumer(Info info) {
this.info = info ;
}
@Override
public void run() {
for (int x = 0 ; x < 100 ; x ++) {
this.info.get();
}
}
} public class TestDemo {
public static void main(String[] args) throws Exception {
Info info = new Info() ;
new Thread(new Producer(info)).start();
new Thread(new Consumer(info)).start();
}
}

通过“同步方法”,解决了数据不同步的问题,但是于此而来的问题就是:数据的重复操作。

针对上两例程序,我们通过Object类的支持,来解决数据重复操作的问题:
  如果像上例的设计,需要在程序中加入一个等待机制;当数据未取则等待数据取出后在存入,当数据未存等待数据存入后取出。而Object类中提供有专门的“等待”。

等待:    public final void wait() throws InterruptedException
唤醒第一个等待线程:    public final void notify() ;
唤醒全部的等待进入:    public final void notifyAll();  //优先级高越有可能先唤醒

通过Object的线程等待和唤醒功能完善程序:

 package hello;

 class Info {  // 数据类
private String title ;
private String content;
private boolean flag = true ;
// true:表示可以生产,不可以取走
// false:表示不可以生产,可以取走
public synchronized void set(String title , String content) {
if (this.flag == false) { // 发现不可以生产,则等待线程
try {
super.wait(); // 通过super调用自己的超类(父类)Object类中的wait()方法等待线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.title = title ;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.content = content ;
this.flag = false ;// 修改标记
super.notify(); //唤醒其他等待线程
}
public synchronized void get() {
if (this.flag == true) {
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.title + "——" + this.content);
this.flag = true ; //修改标记
super.notify(); // 唤醒其他线程
} } class Producer implements Runnable { // 生成者类(线程)
private Info info ;
public Producer(Info info) {
this.info = info ;
}
@Override
public void run() {
for (int x = 0 ; x < 100 ; x ++ ) {
if ( x % 2 == 0 ) {
this.info.set("张三", "男");
} else {
this.info.set("李悦", "女");
}
}
}
} class Consumer implements Runnable {
private Info info ;
public Consumer(Info info) {
this.info = info ;
}
@Override
public void run() {
for (int x = 0 ; x < 100 ; x ++) {
this.info.get();
}
}
} public class TestDemo {
public static void main(String[] args) throws Exception {
Info info = new Info() ;
new Thread(new Producer(info)).start();
new Thread(new Consumer(info)).start();
}
}

我们依靠Object类中的等待唤醒机制完成了代码的要求。

------------------------

Java 实践:生产者与消费者的更多相关文章

  1. Windows下RabbitMQ 的下载、配置、Java实现生产者和消费者例子

    RabbitMQ是一个轻量级的消息代理中间件,支持多种消息通信协议,支持分布式部署,支持运行于多个操作系统,具有灵活.高可用等特性.RabbitMQ支持多种协议,其中最为重要的是高级消息队列协议(AM ...

  2. java实现生产者和消费者问题

    Java实现生产者和消费者问题 欢迎访问我的个人博客,获取更多有用的东西 链接一 链接二 也可以关注我的微信订阅号:CN丶Moti

  3. Java实现生产者和消费者

    生产者和消费者问题是操作系统的经典问题,在实际工作中也常会用到,主要的难点在于协调生产者和消费者,因为生产者的个数和消费者的个数不确定,而生产者的生成速度与消费者的消费速度也不一样,同时还要实现生产者 ...

  4. java之生产者与消费者

    package com.produce; import java.util.LinkedList; import java.util.Queue; /*@author shijin * 生产者与消费者 ...

  5. java线 生产者和消费者

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGlhbmdydWkxOTg4/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  6. Java实现生产者与消费者模式

    生产者不断向队列中添加数据,消费者不断从队列中获取数据.如果队列满了,则生产者不能添加数据:如果队列为空,则消费者不能获取数据.借助实现了BlockingQueue接口的LinkedBlockingQ ...

  7. Java中生产者与消费者模式

    生产者消费者模式 首先来了解什么是生产者消费者模式.该模式也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例.该问题描述了两个共享固定大小缓冲区的线 ...

  8. java之生产者和消费者问题

    package testThread; public class Test3 { public static void main(String[] args) { Clerk c = new Cler ...

  9. 菜鸡的Java笔记 生产者与消费者

    生产者与消费者        代码要求知道做什么用即可        线程间的通讯问题以及 Object 类的支持            基础模型        现在希望实现一种数据的生产和取出的操作 ...

  10. Java 多线程-生产者、消费者

    一.整体代码 ThreadDemo.java public class ThreadDemo { public static void main(String[] args) { Godown god ...

随机推荐

  1. 分析Jackson的安全漏洞CVE-2019-12086

    CVE-2019-12086 Description A Polymorphic Typing issue was discovered in FasterXML jackson-databind 2 ...

  2. python自带编译器在写入文件时闪退,或者一步步执行到写入时提示8170。解决办法:

    用python黑框运行程序写入文件时闪退,或一行行运行到写入时提示8170数字. 经试验,为文件路径错误导致. with open("1.doc", "wb") ...

  3. MySQLl存储过程学习总结

    1.简介 : 逻辑处理一般不是一条语句组成,需要多条之间相互配合使用              这时,存储过程就是为了以后使用而保存的的一条或多条Mysql语句的集合 2.为何 : 1)简单:将处理单 ...

  4. 清新水彩追梦在路上述职报告通用PPT模板

    清新漫画风,轻松可爱模版来源:  http://ppt.dede58.com/gongzuohuibao/26566.html

  5. PHP fnmatch 文件系统函数

    定义和用法 fnmatch - 用模式匹配文件名 目前该函数无法在 Windows 或其它非 POSIX 兼容的系统上使用. 版本支持 PHP4 PHP5 PHP7 4.3.0(含)+支持 支持 支持 ...

  6. SAP系统邮件功能配置

    相信SAP顾问或多或少都会接到用户要求SAP系统能够定时发送邮件的功能,定时将用户需要的信息已邮件的方式发送给固定的人员. 下面就来讲一下SAP发送邮件应该如何配置: 1.RZ10做配置: is/SM ...

  7. 使用Jitpack发布自己的Android Library

    版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/169 使用Jitpack发布自己的Android Libr ...

  8. Thymeleaf常用语法:HTML属性设置

    使用Thymeleaf的属性来设置HTML属性.(1)使用th:attr属性可以修改原来HTML节点的属性:(2)th:attr属性可以同时设置多个属性:(3)每一个HTML属性都有对应的Thymel ...

  9. 学习postman教程

    postman可以做什么 1.可以做单接口的测试 2.可以调试接口 3.对接口设置变量后,可以做多接口的测试,并输出报告 如何用postman调试接口 1.填写api地址 2.选择请求方式 3.输入a ...

  10. Druid-代码段-4-3

    所属文章:池化技术(一)Druid是如何管理数据库连接的? 本代码段对应流程4.2,防止内存泄漏的连接关闭检测: //回收长期未归还的连接(再次说明:该方法仅在removeAbandoned设置为tr ...