`
尤文武
  • 浏览: 8984 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

wait和notify实现生产者消费者模型

阅读更多
       读这篇文章之前,首先要弄明白java对象的两个方法,wait和notify或者notifyAll,那弄懂这两个方法又要知道一个概念,java中Object类有个对象锁,所有的对象都继承自Object类,因此每个对象都有个锁,而且java中的对象锁在同一时刻只能由一个线程持有,这是java在多线程编程中实现互斥的基础。 那一个线程如何获得一个对象的锁呢?根据JDK源码,一个线程可以有3种方法获得对象锁,第一种是执行该对象实例的synchronized方法,第二种是执行对象中的synchronized代码块,第三种是执行类(非实例)的静态synchronized方法,最后需要注意的是一个对象的锁在同一个时间只能由一个线程持有,我们今天将要讨论的3个方法就是在线程获得了对象锁的情况下执行的。

        先说一下wait方法,该方法的作用就是暂停当前线程,并释放占有的对象锁(当前线程一定要占有对象锁,否则会抛出异常),区别于sleep方法,sleep方法是Thread类的方法,也是暂停当前线程,但并不会释放锁(如果占有的话)。

        接下来说notifyAll方法,作用是唤醒所有等待当前线程持有的对象锁的线程,同样的道理,调用notifyAll方法的线程必须持有对象锁,对象锁并不会在调用完notifyAll方法之后立刻被释放,需要等待当前线程(或者持有对象锁的方法,代码块)结束后才会被释放,锁被释放后,其它被唤醒的线程就会随机竞争,先得到该对象锁的线程将会进入执行, notify和notifyAll的区别就是notify只会随机唤醒一个线程,notifyAll会唤醒所有调用wait的线程。

       以下是运用wait和notify实现的生产者消费者的代码


package cn.javass.web.controller;

import java.util.Stack;

public class TestThead2
{
	public static void main(String[] args)
	{
		ProductStack ps = new ProductStack();
		Thread p = new Thread(new Producer(ps));
		Thread c = new Thread(new Consumer(ps));
		
		p.start();
		c.start();
	}
}
//模拟一个产品,包括一个属性id
class Product
{
	String id;
	
	Product(String id)
	{
		this.id = id;
	}
}
//仓库类
class ProductStack
{
	//仓库有个最大存储容量
	int index = 6;
	
	//仓库中货架,通过java.util.Stack来实现,主要用到push和pop两个方法
	Stack<Product> products = new Stack<Product>();
	
	//生产产品,并放入货架
	public synchronized void push(Product pc)
	{
		//如果货架上的产品已经满了,则停止生产
		if (products.size() == index)
		{
			try
			{
				//一旦调用wait方法,当前线程将暂停,同时释放ProductStack的对象锁,
				//直到其它拥有该对象锁的线程调用notify或者notifyAll,当前线程才会
				//尝试再次获取ProductStack的对象锁,并从此处继续执行
				wait();
			}
			catch (InterruptedException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		//生产一个产品
		products.push(pc);
		
		System.out.println("生产了 " + pc.id);
		
		//此时货架肯定不是空的,通知消费者消费, 之前讲到,notify和notifyAll的区别是
		//前者随机唤醒一个等待ProductStack对象锁的线程,而后者是唤醒所有线程,此时当前
		//进程中只有pop一个线程在等待对象锁,因此此处也可以使用notify,
		//思考一下,如果把notifyAll方法拿到products.push(pc);前面呢?
		//其实效果是一样的,上面说过,调用notify或者notifyAll方法虽然唤醒了等待线程,
		//但并不会释放对象锁,直到当前线程(或者持有对象锁的方法,代码块)结束后才会被释放,
		//因此pop线程虽然被唤醒,但由于拿不到ProductStack对象锁,依旧无法执行
		notifyAll();
	}
	
	//从货架上取出产品供消费
	public synchronized void pop()
	{
		//如果货架上是空的,则停止消费
		if (products.isEmpty())
		{
			try
			{
				wait();
			}
			catch (InterruptedException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//消费一个产品
		Product pc = products.pop();
		
		System.out.println("消费了 " + pc.id);
		//此时货架肯定不是满的,通知生产者生产
		notifyAll();
	}
}

//生产者
class Producer implements Runnable
{
	ProductStack ps;
	
	Producer(ProductStack ps)
	{
		this.ps = ps;
	}
	
	@Override
	public void run()
	{
		//本次生产计划为20件商品
		for (int i = 0; i < 20; i++)
		{
			ps.push(new Product(String.valueOf(i)));
		}
	}
	
}

//消费者
class Consumer implements Runnable
{
	ProductStack ps;
	
	Consumer(ProductStack ps)
	{
		this.ps = ps;
	}
	
	@Override
	public void run()
	{
		//消费计划为20件商品
		for (int i =0;i<20;i++)
		{
			ps.pop();
			
			//消费完一个商品之后停顿一段时间,接着消费,可以把这段代码注释掉,比较下输出结果有什么不同
			try 
			{
                Thread.sleep((int) (Math.random() * 20));
            } 
			catch (InterruptedException e) 
			{
                e.printStackTrace();
            }
		}
	}
	
}




由于ProductStack的push和pop方法都是synchronized的,而前面我们介绍synchronized方法要求必须获取对象锁才能执行,而同一时间又只能有一个线程可以获取对象锁,因此同一个时间只能有一个方法执行,要么在生产,要么在消费。
分享到:
评论
1 楼 jianjieyan 2015-05-13  
顶,牛逼,向大神致敬,以后常来,

相关推荐

    Java 线程间通信,生产者与消费者模型

    使用wait()和notify()实现的生产者与消费者模型,可以了解如何使用wait()和notify()进行线程间通信。(上一次上传的代码有一个问题没有考虑到,这次修补了——CSDN没法撤销资源,只能再上传了)

    Java实现生产者消费者问题

     生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,如下图所示,生产者向空间里存放数据,而消费者取用数据,如果不加以协调可能会出现以下情况:  生产者消费者...

    Java并发编程原理与实战

    通过生产者消费者模型理解等待唤醒机制.mp4 Condition的使用及原理解析.mp4 使用Condition重写waitnotify案例并实现一个有界队列.mp4 深入解析Condition源码.mp4 实战:简易数据连接池.mp4 线程之间通信之join应用与...

    java并发编程

    第30节通过生产者消费者模型理解等待唤醒机制00:20:50分钟 | 第31节Condition的使用及原理解析00:17:40分钟 | 第32节使用Condition重写wait/notify案例并实现一个有界队列00:22:05分钟 | 第33节深入解析Condition...

    龙果 java并发编程原理实战

    第30节通过生产者消费者模型理解等待唤醒机制00:20:50分钟 | 第31节Condition的使用及原理解析00:17:40分钟 | 第32节使用Condition重写wait/notify案例并实现一个有界队列00:22:05分钟 | 第33节深入解析Condition...

    Java 并发编程原理与实战视频

    第30节通过生产者消费者模型理解等待唤醒机制00:20:50分钟 | 第31节Condition的使用及原理解析00:17:40分钟 | 第32节使用Condition重写wait/notify案例并实现一个有界队列00:22:05分钟 | 第33节深入解析Condition...

    龙果java并发编程完整视频

    第30节通过生产者消费者模型理解等待唤醒机制00:20:50分钟 | 第31节Condition的使用及原理解析00:17:40分钟 | 第32节使用Condition重写wait/notify案例并实现一个有界队列00:22:05分钟 | 第33节深入解析Condition...

    汪文君高并发编程实战视频资源全集

    │ 高并发编程第一阶段26讲、多线程下的生产者消费者模型,以及详细介绍notifyAll方法.mp4 │ 高并发编程第一阶段27讲、wait和sleep的本质区别是什么,深入分析(面试常见问题).mp4 │ 高并发编程第一阶段28讲、...

    汪文君高并发编程实战视频资源下载.txt

    │ 高并发编程第一阶段26讲、多线程下的生产者消费者模型,以及详细介绍notifyAll方法.mp4 │ 高并发编程第一阶段27讲、wait和sleep的本质区别是什么,深入分析(面试常见问题).mp4 │ 高并发编程第一阶段28讲、...

    javaSE代码实例

    16.4.5 “生产者-消费者”案例的实际运行 365 16.4.6 notify方法的使用 366 16.4.7 同步的语句块 367 16.4.8 线程的死锁 369 16.4.9 防止错误的使用wait、notify、notifyAll方法 371 16.5 获取当前正在...

    外文翻译 stus MVC

    This stateless behavior made it difficult for the model to notify the view of changes. On the Web, the browser has to re-query the server to discover modification to the state of the application. ...

    JAVA核心知识点整理(有效)

    25 3:ServicorTo 和 ServicorFrom 互换................................................................................................................25 2.3.3.1. 2.4.1. 如何确定垃圾 ......................

    JAVA程序设计教程

    第一章程序和程序设计 .......................................................................................................1 §1.1 什么是程序 ?........................................................

Global site tag (gtag.js) - Google Analytics