生产者与消费者(一)---wait与notify
生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。解决生产者/消费者问题的方法可分为两类:
(1)采用某种机制保护生产者和消费者之间的同步;
(2)在生产者和消费者之间建立一个管道。
第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。第二种管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强。因此本文只介绍同步机制实现的生产者/消费者问题。
同步问题核心在于:如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。Java语言在多线程编程上实现了完全对象化,提供了对同步机制的良好支持。在Java中一共有四种方法支持同步,其中前三个是同步方法,一个是管道方法。
(1)wait() / notify()方法
(2)await() / signal()方法
(3)BlockingQueue阻塞队列方法
(4)PipedInputStream / PipedOutputStream
本文先介绍第一种wait() / notify()方法,其他三种后面再讨论。
wait() / notify()方法
wait() / nofity()方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。
wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让其他线程执行。
notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
代码示例:
缓冲区(仓库):
public class Storage {
    // 仓库最大存储量
    private final int MAX_SIZE = 100;  
    // 仓库存储的载体
    private LinkedList<Object> list = new LinkedList<Object>();  
    // 生产num个产品
    public void produce(int num){
        // 同步代码段
        synchronized (list){
            // 如果仓库剩余容量不足
            while (list.size() + num > MAX_SIZE){
                System.out.println("【要生产的产品数量】:" + num + "/t【库存量】:"+ list.size() + "/t暂时不能执行生产任务!");
                try{
                    // 由于条件不满足,生产阻塞
                    list.wait();
                }
                catch (InterruptedException e){
                    e.printStackTrace();
                }
            }  
            // 仓库剩余容量充足,即生产条件满足情况下,生产num个产品
            for (int i = 1; i <= num; ++i){
                list.add(new Object());
            }  
            System.out.println("【已经生产产品数】:" + num + "/t【现仓储量为】:" + list.size());  
            list.notifyAll(); //生产完产品后,通知其他被阻塞的线程
        }
    }  
    // 消费num个产品
    public void consume(int num){
        // 同步代码段
        synchronized (list){
            // 如果仓库存储量不足
            while (list.size() < num){
                System.out.println("【要消费的产品数量】:" + num + "/t【库存量】:"+ list.size() + "/t暂时不能执行消费任务!");
                try{
                    // 由于条件不满足,消费阻塞
                    list.wait();
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }  
            // 消费条件满足情况下,消费num个产品
            for (int i = 1; i <= num; ++i){
                list.remove();
            }  
            System.out.println("【已经消费产品数】:" + num + "/t【现仓储量为】:" + list.size());  
            list.notifyAll();//消费完后,释放锁,通知其他被阻塞的线程
        }
    }  
    // get/set方法
    public LinkedList<Object> getList(){
        return list;
    }  
    public void setList(LinkedList<Object> list){
        this.list = list;
    }  
    public int getMAX_SIZE(){
        return MAX_SIZE;
    }
}
生产者:
public class Producer extends Thread {
    // 每次生产的产品数量
    private int num;  
    // 所在放置的仓库
    private Storage storage;  
    // 构造函数,设置仓库
    public Producer(Storage storage){
        this.storage = storage;
    }  
    // 线程run函数
    public void run(){
        produce(num);
    }  
    // 调用仓库Storage的生产函数
    public void produce(int num){
        storage.produce(num);
    }  
    // get/set方法
    public int getNum()
    {
        return num;
    }  
    public void setNum(int num)
    {
        this.num = num;
    }  
    public Storage getStorage()
    {
        return storage;
    }  
    public void setStorage(Storage storage)
    {
        this.storage = storage;
    }  
}
消费者:
public class Consumer extends Thread {
    // 每次消费的产品数量
    private int num;  
    // 所在放置的仓库
    private Storage storage;  
    // 构造函数,设置仓库
    public Consumer(Storage storage){
        this.storage = storage;
    }  
    // 线程run函数
    public void run(){
        consume(num);
    }  
    // 调用仓库Storage的生产函数
    public void consume(int num){
        storage.consume(num);
    }  
    // get/set方法
    public int getNum()
    {
        return num;
    }  
    public void setNum(int num)
    {
        this.num = num;
    }  
    public Storage getStorage()
    {
        return storage;
    }  
    public void setStorage(Storage storage)
    {
        this.storage = storage;
    }
}
测试类:
public class Test {
    public static void main(String[] args) {
        // 仓库对象
        Storage storage = new Storage();  
        // 生产者对象
        Producer p1 = new Producer(storage);
        Producer p2 = new Producer(storage);
        Producer p3 = new Producer(storage);
        Producer p4 = new Producer(storage);
        Producer p5 = new Producer(storage);
        Producer p6 = new Producer(storage);
        Producer p7 = new Producer(storage);  
        // 消费者对象
        Consumer c1 = new Consumer(storage);
        Consumer c2 = new Consumer(storage);
        Consumer c3 = new Consumer(storage);  
        // 设置生产者产品生产数量
        p1.setNum(10);
        p2.setNum(10);
        p3.setNum(10);
        p4.setNum(10);
        p5.setNum(10);
        p6.setNum(10);
        p7.setNum(80);  
        // 设置消费者产品消费数量
        c1.setNum(50);
        c2.setNum(20);
        c3.setNum(30);  
        // 线程开始执行
        c1.start();
        c2.start();
        c3.start();
        p1.start();
        p2.start();
        p3.start();
        p4.start();
        p5.start();
        p6.start();
        p7.start();
    }
}
你可能会对Storage类中为什么要定义public void produce(int num);和public void consume(int num);方法感到不解,为什么不直接在生产者类Producer和消费者类Consumer中实现这两个方法,却要调用Storage类中的实现呢?
因为如果在需要更新生产或者消费方法时,只需要更新仓库类Storage的代码即可,生产者Producer、消费者Consumer、测试类Test的代码均不需要进行任何更改。这样我们就知道为什么要在Storage类中定义public void produce(int num);和public void consume(int num);方法,并在生产者类Producer和消费者类Consumer中调用Storage类中的实现了吧。将可能发生的变化集中到一个类中,不影响原有的构架设计,同时无需修改其他业务层代码。无意之中,好像使用了一种叫做策略模式的设计模式!
生产者与消费者(一)---wait与notify的更多相关文章
- JAVA并发实现五(生产者和消费者模式wait和notify方式实现)
		
package com.subject01; import java.util.PriorityQueue; /** * 通过wait和notify 实现 * 生产者-消费者模型:当队列满时,生产者需 ...
 - 生产者与消费者模式-阻塞 wait,notify
		
设计思路:生产者push ,消费者 拿,篮子装,syncstack先进后出,while 判断 index=0 wait, 当 Producer生产了 并push到篮子里 notify(唤醒 ...
 - 【java线程系列】java线程系列之线程间的交互wait()/notify()/notifyAll()及生产者与消费者模型
		
关于线程,博主写过java线程详解基本上把java线程的基础知识都讲解到位了,但是那还远远不够,多线程的存在就是为了让多个线程去协作来完成某一具体任务,比如生产者与消费者模型,因此了解线程间的协作是非 ...
 - Thread(生产者和消费者) wait、notify、notifyAll
		
在java中,线程间的通信可以使用wait.notify.notifyAll来进行控制.从名字就可以看出来这3个方法都是跟多线程相关的,但是可能让你感到吃惊的是:这3个方法并不是Thread类或者是R ...
 - 母鸡下蛋实例:多线程通信生产者和消费者wait/notify和condition/await/signal条件队列
		
简介 多线程通信一直是高频面试考点,有些面试官可能要求现场手写生产者/消费者代码来考察多线程的功底,今天我们以实际生活中母鸡下蛋案例用代码剖析下实现过程.母鸡在鸡窝下蛋了,叫练从鸡窝里把鸡蛋拿出来这个 ...
 - java进阶(40)--wait与notify(生产者与消费者模式)
		
文档目录: 一.概念 二.wait的作用 三.notify的作用 四.生产者消费者模式 五.举例 ---------------------------------------分割线:正文------ ...
 - 第3月第2天 find symbolicatecrash 生产者-消费者 ice 引用计数
		
1.linux find export find /Applications/Xcode.app/ -name symbolicatecrash -type f export DEVELOPER_DI ...
 - java 22 - 19 多线程之生产者和消费者的代码优化
		
在之前,是把生产者录入数据和消费者获取数据的所有代码都分别写在各自的类中. 这样不大好 这次把生产者和消费者部分关键代码都写入资源类中: package zl_Thread; public class ...
 - 线程操作案例--生产者与消费者,Object类对线程的支持
		
本章目标 1)加深对线程同步的理解 2)了解Object类中对线程的支持方法. 实例 生产者不断生产,消费者不断消费产品. 生产者生产信息后将其放到一个区域中,之后消费者从区域中取出数据. 既然生产的 ...
 
随机推荐
- (DT系列五)Linux kernel 是怎么将 devicetree中的内容生成plateform_device
			
Linux kernel 是怎么将 devicetree中的内容生成plateform_device 1,实现场景(以Versatile Express V2M为例说明其过程)以arch/arm/ma ...
 - Bitbucket Pull Request和fork
			
本文参考了http://blog.jobbole.com/76854/ Pull Request在Forking工作流中使用,这个也同样适用于小团队的开发协作和第三方开发者向开源项目的贡献.当你要 ...
 - 运用HBuilder上传到GitHub
			
Hbuilder安装github插件 如图所示: 一.打开自己GitHub,新建一个"库" 2.设置自己项目名和简介 3.建完后,就会显示GitHub要上传路径 4.打开" ...
 - Js- 菜单
			
很简单的JS二级菜单显示,收藏 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "ht ...
 - mysql技术调优资料整理
			
1,15 个有用的 MySQL/MariaDB 性能调整和优化技巧 2,MariaDB设置主从复制 3,CentOS6.4安装mysql2redis http://www.cnblogs ...
 - HTML5 汽泡效果
			
又到了晚上了,精神只有在晚上的时候才能爆发,可能程序员的命吧.废话就不多说了,今天我为大家带来的是一个气泡的效果. 代码下载 下面请看效果图
 - js_面向对象编程
			
主要内容 值类型与引用类型的特征 深拷贝与浅拷贝 对象的动态特性 构造函数的执行过程 异常处理 dom操作(略) <!DOCTYPE html> <html> <head ...
 - ctrl+z的JAVA实现,借助了命令模式(command pattern)
			
前些天学习<<JAVA与模式>>,到命令模式时,随带给了一个CTRL+Z案例的实现,想来学习编程这么久,CTRL+Z还没有认真实现过. 因此,借助JAVA与模式里面的源代码,自 ...
 - Java调用MySQL存储过程
			
Java调用MySQL的存储过程,需要用JDBC连接,环境eclipse 首先查看MySQL中的数据库的存储过程,接着编写代码调用 mysql> show procedure status; + ...
 - [TypeScript] Installing TypeScript and Running the TypeScript Compiler (tsc)
			
This lesson shows you how to install TypeScript and run the TypeScript compiler against a .ts file f ...