JAVA线程消费者与生产者模型

    本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/7433673,转载请注明。 

 

 

  上一篇讲述了线程的互斥(同步),但是在很多情况下,仅仅同步是不够的,还需要线程与线程协作(通信),生产者/消费者问题是一个经典的线程同步以及通信 的案例。该问题描述了两个共享固定大小缓冲区的线程,即所谓的“生产者”和“消费者”在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到 缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空 时消耗数据。要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往 缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者,通常采用线程间通信的方法解决该问题。如果 解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形。

 

  假设有这样一种情况,有一个盘子,盘子里只能放一个鸡蛋,A线程专门往盘子里放鸡蛋,如果盘子里有鸡蛋,则一直等到盘子里没鸡蛋,B线程专门从盘子里取鸡 蛋,如果盘子里没鸡蛋,则一直等到盘子里有鸡蛋。这里盘子是一个互斥区,每次放鸡蛋是互斥的,每次取鸡蛋也是互斥的,A线程放鸡蛋,如果这时B线程要取鸡 蛋,由于A没有释放锁,B线程处于等待状态,进入阻塞队列,放鸡蛋之后,要通知B线程取鸡蛋,B线程进入就绪队列,反过来,B线程取鸡蛋,如果A线程要放 鸡蛋,由于B线程没有释放锁,A线程处于等待状态,进入阻塞队列,取鸡蛋之后,要通知A线程放鸡蛋,A线程进入就绪队列。我们希望当盘子里有鸡蛋时,A线 程阻塞,B线程就绪,盘子里没鸡蛋时,A线程就绪,B线程阻塞,代码如下:

 

import java.util.ArrayList;
import java.util.List;
/** 定义一个盘子类,可以放鸡蛋和取鸡蛋 */
public class Plate {
	/** 装鸡蛋的盘子 */
	List<Object> eggs = new ArrayList<Object>();
	/** 取鸡蛋 */
	public synchronized Object getEgg() {
		while (eggs.size() == 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		Object egg = eggs.get(0);
		eggs.clear();// 清空盘子
		notify();// 唤醒阻塞队列的某线程到就绪队列
		System.out.println("拿到鸡蛋");
		return egg;
	}
	/** 放鸡蛋 */
	public synchronized void putEgg(Object egg) {
		while (eggs.size() > 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		eggs.add(egg);// 往盘子里放鸡蛋
		notify();// 唤醒阻塞队列的某线程到就绪队列
		System.out.println("放入鸡蛋");
	}
	static class AddThread&nbsp;implements Runnable&nbsp; {
		private Plate plate;
		private Object egg = new Object();
		public AddThread(Plate plate) {
			this.plate = plate;
		}
		public void run() {
			plate.putEgg(egg);
		}
	}
	static class GetThread&nbsp;implements Runnable&nbsp; {
		private Plate plate;
		public GetThread(Plate plate) {
			this.plate = plate;
		}
		public void run() {
			plate.getEgg();
		}
	}
	public static void main(String args[]) {
		Plate plate = new Plate();
		for(int i = 0; i < 10; i++) {
			new Thread(new AddThread(plate)).start();
			new Thread(new GetThread(plate)).start();
		}
	}
}

 

 

 

  程序开始,A线程判断盘子是否为空,放入一个鸡蛋,并且唤醒在阻塞队列的一个线程,阻塞队列为空;假设CPU又调度了一个A线程,盘子非空,执行等待,这 个A线程进入阻塞队列;然后一个B线程执行,盘子非空,取走鸡蛋,并唤醒阻塞队列的A线程,A线程进入就绪队列,此时就绪队列就一个A线程,马上执行,放 入鸡蛋;如果再来A线程重复第一步,在来B线程重复第二步,整个过程就是生产者(A线程)生产鸡蛋,消费者(B线程)消费鸡蛋。

 

     大家注意到没有,在调用wait方法时,都是用while判断条件的,而不是if,在wait方法说明中,也推荐使用while,因为在某些特定的情况 下,线程有可能被假唤醒,使用while会循环检测更稳妥。wait和notify方法必须工作于synchronized内部,且这两个方法只能由锁对 象来调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个使用Java语言实现的生产者消费者模型的示例: ``` import java.util.LinkedList; public class ProducerConsumerExample { public static void main(String[] args) { LinkedList<Integer> queue = new LinkedList<>(); int maxSize = 5; Producer producer = new Producer(queue, maxSize); Consumer consumer = new Consumer(queue); Thread producerThread = new Thread(producer, "Producer"); Thread consumerThread = new Thread(consumer, "Consumer"); producerThread.start(); consumerThread.start(); } } class Producer implements Runnable { private LinkedList<Integer> queue; private int maxSize; public Producer(LinkedList<Integer> queue, int maxSize) { this.queue = queue; this.maxSize = maxSize; } @Override public void run() { while (true) { synchronized (queue) { while (queue.size() == maxSize) { try { System.out.println("队列已满,生产者等待消费..."); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } int number = (int) (Math.random() * 100); queue.add(number); System.out.println("生产者生产: " + number); queue.notifyAll(); } } } } class Consumer implements Runnable { private LinkedList<Integer> queue; public Consumer(LinkedList<Integer> queue) { this.queue = queue; } @Override public void run() { while (true) { synchronized (queue) { while (queue.isEmpty()) { try { System.out.println("队列为空,消费者等待生产..."); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } int number = queue.removeFirst(); System.out.println("消费者消费: " + number); queue.notifyAll(); } } } } ``` 上述代码实现了一个简单的生产者消费者模型,其中使用了一个线程安全的`LinkedList`队列作为生产者消费者之间的缓冲区。在`Producer`和`Consumer`类中,`run()`方法被覆盖并实现了生产和消费的逻辑。 在生产者线程中,如果队列已满,则生产者线程将进入等待状态。当队列不满时,生产者线程将生成随机数并将其添加到队列中,并通过调用`notifyAll()`方法通知消费者线程可以消费了。在消费者线程中,如果队列为空,则消费者线程将进入等待状态。当队列不为空时,消费者线程将从队列中删除第一个元素,并通过调用`notifyAll()`方法通知生产者线程可以继续生产。 这种实现方式使用`synchronized`关键字确保在对队列进行修改时线程安全。此外,生产者消费者线程之间的通信使用了`wait()`和`notifyAll()`方法,以确保生产者消费者之间的协调。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值