文档章节

Java并发编程初级篇(十二):使用wait和notify生产者消费者问题

阿拉德大陆的魔法师
 阿拉德大陆的魔法师
发布于 2016/11/24 17:25
字数 1250
阅读 21
收藏 0
点赞 0
评论 0

在这里我们模拟一个生产者消费者问题。定义一个缓冲区,生产者生产数据并存入缓冲区,消费者从缓冲区中消费数据。缓冲区有固定大小,当缓冲区达到最大时生产者被挂起并等待消费者消费数据后再尝试将生产的数据加入缓冲区;当缓冲区数据量为0时,消费者被挂起直到有生产者向缓冲区中存入数据。

我们可以看到这个缓冲区是一个公共变量,所以缓冲区中数据的存放和取出都必须放置在一段synchronized修饰的同步代码中。

Java API的Object类提供了一组方法wait(),notify()和nofityAll()用于实现这个例子。

示例代码:

首先我们创建一个类来模拟实现一个阻塞数据缓冲池,这个缓冲池有一个链表结构用于缓冲数据,有一个整形变量用于定义数据缓冲区大小。一个set()方法用于模拟生产者向缓冲区内加入数据,一旦数据缓冲区满则挂起,成功插入数据后唤起所有消费者线程。一个get()方法用于模拟消费者从缓冲区中消费数据,一旦数据缓冲区空了则挂起,成功消费数据后唤起所有生产者线程。

public class EventStorage {
    private int maxSize;
    private LinkedList<Date> storage;

    public EventStorage() {
        maxSize = 10;
        storage = new LinkedList<Date>();
    }

    public void set() {
        synchronized (Main.producerController) {
            while (storage.size() == maxSize) {
                try {
                    Main.producerController.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            storage.add(new Date());
            System.out.printf("%s: Add one. storge size: %d.\n", Thread.currentThread().getName(), storage.size());
        }

        synchronized (Main.consumerController) {
            Main.consumerController.notifyAll();
        }
    }

    public void get() {
        synchronized (Main.consumerController) {
            while (storage.size() == 0) {
                try {
                    Main.consumerController.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("%s: Get one %s. Storge size: %d.\n", Thread.currentThread().getName(), storage.poll(), storage.size());
        }

        synchronized (Main.producerController) {
            Main.producerController.notifyAll();
        }
    }
}

创建两个线程类用于模拟生产者和消费者。

public class Producer implements Runnable{
    private EventStorage eventStorage;

    public Producer(EventStorage eventStorage) {
        this.eventStorage = eventStorage;
    }

    @Override
    public void run() {
        eventStorage.set();
    }
}
public class Consumer implements Runnable{
    private EventStorage eventStorage;

    public Consumer(EventStorage eventStorage) {
        this.eventStorage = eventStorage;
    }

    @Override
    public void run() {
        eventStorage.get();
    }
}

定义主方法类,在这里我们启动20个消费者线程与20个生产者线程,其中生产者速度快,消费者速度慢。这样会导致生产者线程阻塞,等待消费者消费数据后被唤醒。

public class Main {
    public static final Object producerController = new Object();
    public static final Object consumerController = new Object();

    public static void main(String[] args) {
        EventStorage eventStorage = new EventStorage();

        Producer producer = new Producer(eventStorage);
        Consumer consumer = new Consumer(eventStorage);

        Thread[] threads1 = new Thread[20];
        Thread[] threads2 = new Thread[20];

        for (int i = 0; i < 20; i++) {
            threads1[i] = new Thread(producer, "Producer-" + i);
            threads2[i] = new Thread(consumer, "Consumer-" + i);
        }

        for (int i = 0; i < 20; i++) {
            threads1[i].start();
            threads2[i].start();
        }
    }
}

查看控制台日日志,你会发现生产者快速加入10个数据到缓冲区,然后线程开始挂起,直到消费者开始消费数据,每次消费数据后生产者会被唤醒一次,并加入数据到缓冲区。最后20个生产者线程都执行完毕后,消费者线程才执行完毕。如果你想模拟消费者被阻塞只需要调整生产者速度慢于消费者速度。

Producer-0: Add one. storge size: 1.
Producer-19: Add one. storge size: 2.
Producer-18: Add one. storge size: 3.
Producer-17: Add one. storge size: 4.
Producer-16: Add one. storge size: 5.
Producer-15: Add one. storge size: 6.
Producer-14: Add one. storge size: 7.
Producer-13: Add one. storge size: 8.
Producer-12: Add one. storge size: 9.
Producer-11: Add one. storge size: 10.
Consumer-3: Get one Thu Nov 24 17:12:13 CST 2016. Storge size: 9.
Producer-1: Add one. storge size: 10.
Consumer-19: Get one Thu Nov 24 17:12:13 CST 2016. Storge size: 9.
Producer-10: Add one. storge size: 10.
Consumer-18: Get one Thu Nov 24 17:12:13 CST 2016. Storge size: 9.
Producer-2: Add one. storge size: 10.
Consumer-17: Get one Thu Nov 24 17:12:13 CST 2016. Storge size: 9.
Producer-9: Add one. storge size: 10.
Consumer-16: Get one Thu Nov 24 17:12:13 CST 2016. Storge size: 9.
Producer-3: Add one. storge size: 10.
Consumer-15: Get one Thu Nov 24 17:12:13 CST 2016. Storge size: 9.
Producer-8: Add one. storge size: 10.
Consumer-14: Get one Thu Nov 24 17:12:13 CST 2016. Storge size: 9.
Producer-4: Add one. storge size: 10.
Consumer-13: Get one Thu Nov 24 17:12:13 CST 2016. Storge size: 9.
Producer-7: Add one. storge size: 10.
Consumer-12: Get one Thu Nov 24 17:12:13 CST 2016. Storge size: 9.
Producer-5: Add one. storge size: 10.
Consumer-11: Get one Thu Nov 24 17:12:13 CST 2016. Storge size: 9.
Producer-6: Add one. storge size: 10.
Consumer-10: Get one Thu Nov 24 17:12:14 CST 2016. Storge size: 9.
Consumer-9: Get one Thu Nov 24 17:12:15 CST 2016. Storge size: 8.
Consumer-8: Get one Thu Nov 24 17:12:16 CST 2016. Storge size: 7.
Consumer-7: Get one Thu Nov 24 17:12:17 CST 2016. Storge size: 6.
Consumer-6: Get one Thu Nov 24 17:12:18 CST 2016. Storge size: 5.
Consumer-5: Get one Thu Nov 24 17:12:19 CST 2016. Storge size: 4.
Consumer-4: Get one Thu Nov 24 17:12:20 CST 2016. Storge size: 3.
Consumer-2: Get one Thu Nov 24 17:12:21 CST 2016. Storge size: 2.
Consumer-1: Get one Thu Nov 24 17:12:22 CST 2016. Storge size: 1.
Consumer-0: Get one Thu Nov 24 17:12:23 CST 2016. Storge size: 0.

 

© 著作权归作者所有

共有 人打赏支持
阿拉德大陆的魔法师
粉丝 20
博文 91
码字总数 83019
作品 0
西城
程序员
Java多线程学习(四)等待/通知(wait/notify)机制

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀 ⋅ 04/16 ⋅ 0

你真的懂wait、notify和notifyAll吗

生产者消费者模型是我们学习多线程知识的一个经典案例,一个典型的生产者消费者模型如下: 这段代码很容易引申出来两个问题:一个是wait()方法外面为什么是while循环而不是if判断,另一个是结...

A_客 ⋅ 06/03 ⋅ 0

java基础thread——java5之后的多线程(浅尝辄止)

承上启下 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象L...

潇潇漓燃 ⋅ 06/03 ⋅ 0

Java编程的逻辑 -- 并发章 -- 线程的基本协作机制

线程的基本协作 线程的基本协作示例 总结 线程的基本协作 多线程间除了竞争访问同一资源外,也经常需要相互协作的去执行一些任务。而对于协作的基本机制用的最多的无疑是wait/notify。 协作的...

HikariCP ⋅ 06/22 ⋅ 0

Java并发基础你需要知道的基础知识

多线程和并发编程是Java里面的核心内容,通常有以下一些概念需要重点掌握。 线程; 锁; 同步器; 并发容器和框架; Java并发工具类; 原子操作类; Executor框架(执行机制); 并发基础概念 ...

异步社区 ⋅ 05/30 ⋅ 0

Java面试需要准备哪些多线程并发的技术要点

一、概念 什么是线程 一个线程要执行任务,必须得有线程 一个进程(程序)的所有任务都在线程中执行的 一个线程执行任务是串行的,也就是说一个线程,同一时间内,只能执行一个任务 多线程原理 同一...

码蚁说架构 ⋅ 05/31 ⋅ 0

Java并发编程初级篇(十六):Lock+Condition实现生产者消费者问题

之前我们在“Java并发编程初级篇(十二):使用wait和notify生产者消费者问题”,已经使用Java提供的synchronized关键字和wait(),notify(),notifyAll()方法实现过来生产者消费者问题。Java ...

阿拉德大陆的魔法师 ⋅ 2016/11/25 ⋅ 0

【Java并发专题】27篇文章详细总结Java并发基础知识

努力的意义,就是,在以后的日子里,放眼望去全是自己喜欢的人和事! github:https://github.com/CL0610/Java-concurrency,欢迎题issue和Pull request。所有的文档都是自己亲自码的,如果觉...

你听___ ⋅ 05/06 ⋅ 0

Kotlin语言中的泛型设计哲学

Kotlin语言的泛型设计很有意思,但并不容易看懂。关于这个部分的官方文档,我反复看了好几次,终于弄明白Kotlin语言泛型设计的背后哲学。这篇文章将讲述Kotlin泛型设计的整个思考过程及其背后...

欧阳锋 ⋅ 04/16 ⋅ 0

Java多线程学习(五)线程间通信知识点补充

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀 ⋅ 04/16 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

个人博客的运营模式能否学习TMALL天猫质量为上?

心情随笔|个人博客的运营模式能否学习TMALL天猫质量为上? 中国的互联网已经发展了很多年了,记得在十年前,个人博客十分流行,大量的人都在写博客,而且质量还不错,很多高质量的文章都是在...

原创小博客 ⋅ 今天 ⋅ 0

JavaScript零基础入门——(十一)JavaScript的DOM操作

JavaScript零基础入门——(十一)JavaScript的DOM操作 大家好,欢迎回到我们的JavaScript零基础入门。最近有些同学问我说,我讲的的比书上的精简不少。其实呢,我主要讲的是我在开发中经常会...

JandenMa ⋅ 今天 ⋅ 0

volatile和synchronized的区别

volatile和synchronized的区别 在讲这个之前需要先了解下JMM(Java memory Model :java内存模型):并发过程中如何处理可见性、原子性、有序性的问题--建立JMM模型 详情请看:https://baike.b...

MarinJ_Shao ⋅ 今天 ⋅ 0

深入分析Kubernetes Critical Pod(一)

Author: xidianwangtao@gmail.com 摘要:大家在部署Kubernetes集群AddOn组件的时候,经常会看到Annotation scheduler.alpha.kubernetes.io/critical-pod"="",以表示这是一个关键服务,那你知...

WaltonWang ⋅ 今天 ⋅ 0

原子性 - synchronized关键词

原子性概念 原子性提供了程序的互斥操作,同一时刻只能有一个线程能对某块代码进行操作。 原子性的实现方式 在jdk中,原子性的实现方式主要分为: synchronized:关键词,它依赖于JVM,保证了同...

dotleo ⋅ 今天 ⋅ 0

【2018.06.22学习笔记】【linux高级知识 14.4-15.3】

14.4 exportfs命令 14.5 NFS客户端问题 15.1 FTP介绍 15.2/15.3 使用vsftpd搭建ftp

lgsxp ⋅ 今天 ⋅ 0

JeeSite 4.0 功能权限管理基础(Shiro)

Shiro是Apache的一个开源框架,是一个权限管理的框架,实现用户认证、用户授权等。 只要有用户参与一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户...

ThinkGem ⋅ 昨天 ⋅ 0

python f-string 字符串格式化

主要内容 从Python 3.6开始,f-string是格式化字符串的一种很好的新方法。与其他格式化方式相比,它们不仅更易读,更简洁,不易出错,而且速度更快! 在本文的最后,您将了解如何以及为什么今...

阿豪boy ⋅ 昨天 ⋅ 0

Python实现自动登录站点

如果我们想要实现自动登录,那么我们就需要能够驱动浏览器(比如谷歌浏览器)来实现操作,ChromeDriver 刚好能够帮助我们这一点(非谷歌浏览器的驱动有所不同)。 一、确认软件版本 首先我们...

blackfoxya ⋅ 昨天 ⋅ 0

线性回归原理和实现基本认识

一:介绍 定义:线性回归在假设特证满足线性关系,根据给定的训练数据训练一个模型,并用此模型进行预测。为了了解这个定义,我们先举个简单的例子;我们假设一个线性方程 Y=2x+1, x变量为商...

wangxuwei ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部