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

问题引出:

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

需求引出:

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

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

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

 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. IO多路复用?我所理解的IO模式

    1:IO的过程 当我们调用系统函数read时,一般会经历两个阶段: 1:等待数据准备(waiting for the data be ready) 2:将数组从内核拷贝到进程(从内核态到用户态)(co ...

  2. Java中Atomic类的使用分析

    1:为什么会出现Atomic类 在多线程或者并发环境中,我们常常会遇到这种情况 int i=0; i++ 稍有经验的同学都知道这种写法是线程不安全的.为了达到线程安全的目的,我们通常会用synchro ...

  3. Java集合专题

    1.Java 中常用的容器有哪些? 常见容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表. Collectio ...

  4. CSS 从大图中选取部分区域作为目标图标

    从大图中选取部分区域作为目标图标 by:授客 QQ:1033553122 1.图片素材 图片素材如下(大小:137px * 264px),图中从上到下,连续存放了两张100px * 100px的图   ...

  5. opencv-python 图像处理(五)

    Canny边缘检测 1) 使用高斯滤波器,以平滑图像,滤除噪声. 2) 计算图像中每个像素点的梯度强度和方向. 3) 应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测 ...

  6. react-native自定义Modal模态框|仿ios、微信弹窗RN版

    前序 纵观每个优质项目,无论web端还是native原生应用开发,弹窗都是不可忽视的一环,能很大程度上直接决定用户体验.如:微信.支付宝.ios都有很成熟的一套弹窗UI展示场景. 最近一直沉迷在rea ...

  7. udp协议,进程(同步,异步)

    udp协议与进程 一.udp协议 QQ聊天室 #- sever import socket #socket.SOCK_DGRAM--->UPD协议 sever = socket.socket(t ...

  8. sqlserver查看表空间

    sqlserver 用于查看当前数据库所有表占用空间大小的存储过程 create procedure dbo.proc_getsize as begin create table #temp ( t_ ...

  9. python连数据库制作音乐软件

    import pymysql conn = pymysql.connect(host="localhost",user="root",password=&quo ...

  10. 关于git远程

    1.注册github的账户(官网;github.com ) 2.新建一个项目(在右上角点击+,选择New repository ),输入项目名和项目描述,其他可根据自己项目选填,创建完成后可查看到ht ...