您的位置:首页 > 博客中心 > 互联网 >

生产者与消费者问题

时间:2022-05-03 03:06

***********************************************声明******************************************************

      原创作品,出自 “” 博客,欢迎转载,转载时请务必注明出处()。

      由于各种原因,可能存在诸多不足,欢迎斧正!

*********************************************************************************************************

     在进行多线程编程的过程中,线程间的同步与互斥是件需要认真考虑的关键点,而生产者与消费者就是线程间同步的典型例子:若干个生产者在生产产品,若干个消费者同时消费这些物品,保证多个生产者和多个消费者能并发或并行(关于并行与并发的区别以及多线程编程的一些基本概念,可以参考这篇博文:)执行。解决生产者与消费者的典型方法是设立一个产品缓冲区,然后多个生产者与消费者互斥的访问该缓冲区。 

1、模型分析

     生产者与消费者问题本质上是一个同步与互斥问题,其需要注意的地方总结出来有两点:

1)、同步:当缓冲区不满时生产者线程可以继续执行,生产者执行的结果是向缓冲区添加产品;缓冲区不空时消费者线程可以继续执行,消费者执行的结果是从缓冲区取出产品。生产者与消费者可以相互穿插地执行,提高系统效率,还原情景真实性。

2)、互斥:由于生产者与消费者都可以改变缓冲区的状态,所以对缓冲区的操作应该互斥地进行,即不能同时读取相同的变量,避免发生读写冲突。

    由于缓冲区的大小设定、生产者与消费者的数量都会影响该类问题的难度。在此为了简化起见,假定生产者与消费者都顺序操作缓冲区的产品。

2、源代码

import java.util.ArrayList;
import java.util.List;

class ProductBuffer {
	private List m_listProduct = new ArrayList();
	private int m_nSize;

	public ProductBuffer(int size) {
		m_nSize = size;
		m_listProduct.clear();

	}

	synchronized int get() {
		while (m_listProduct.size() <= 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				System.out.println(e);
			}
		}
		int retValue = m_listProduct.get(m_listProduct.size() - 1);
		m_listProduct.remove(m_listProduct.size() - 1);
		notifyAll();
		return retValue;
	}

	synchronized void put(int mon) {
		while (m_listProduct.size() >= m_nSize) {
			try {
				wait();
			} catch (InterruptedException e) {
				System.out.println(e);
			}
		}
		m_listProduct.add(mon);
		notifyAll();
	}
}

class Producer extends Thread {
	private ProductBuffer m_Product;
	private int m_nValue;

	Producer(ProductBuffer pro, int value) {
		m_Product = pro;
		m_nValue = value;
	}

	public void run() {
		m_Product.put(m_nValue);
		System.out.println("生产者中 product为 " + m_nValue);
		try {
			sleep(100);
		} catch (InterruptedException e) {
			System.out.println(e);
		}
	}
}

class Consumer extends Thread {
	private ProductBuffer m_Product;

	Consumer(ProductBuffer pro) {
		m_Product = pro;
	}

	public void run() {
		int val = m_Product.get();
		System.out.println("消费者中 product为 " + val);
	}
}

public class ProducerAndConsumer {
	public static void main(String[] agr) {
		ProductBuffer pro = new ProductBuffer(10);
		Producer[] myPro = new Producer[10];
		for (int i = 0; i < 10; ++i) {
			myPro[i] = new Producer(pro, i);
		}
		Consumer[] myCon = new Consumer[10];
		for (int i = 0; i < 10; ++i) {
			myCon[i] = new Consumer(pro);
		}

		for (int i = 0; i < 10; ++i) {
			myPro[i].start();
			myCon[i].start();
		}
	}
}
     上述代码的功能实现了多个生产者与多个消费者的同步与互斥问题,但生产者与生产者、生产者与消费者、消费者与消费者之间不同同时访问缓冲区,这点是有别于读者与写者问题的。

3、注意事项

1)、JAVA集合只能存放引用类型的的数据,不能存放基本数据类型 ,所以如下的代码段是有错的:

private List m_listProduct = new ArrayList();

应该为:

private List  m_listProduct = new ArrayList();

2)、synchronized的使用

      synchronized可用于锁定一个对象或一个方法,声明任何时候只能有一个线程访问其所修饰的一段代码块或一个方法,另外线程可以访问没被声明为synchronized的代码块或方法。在Java中,不仅类实例对象可以用修饰synchronized,类本身也对应一把锁用修饰synchronized,即类的静态成员函数声明为synchronized,以控制其对类的静态成员变量的访问。synchronized 方法的缺陷:若将一个大的方法声明为synchronized将会大大影响效率,可以通过尽可能的缩小synchronized修饰的段代码范围来提高效率。

3)、wait()、notifyAll()方法的使用

      在Java中,notifyAll、wait方法继承自所有类共有的基类Object。

A、wait()

     导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法。

B、notifyAll()

     唤醒在此对象监视器上等待的所有线程,线程通过调用其中一个 wait 方法,在对象的监视器上等待。

A、有synchronized的地方不一定有wait(),notify()
B、有wait(),notify()的地方必有synchronized。因为wait()和notify()不是属于线程类,而是每一个对象都具有的方法,而且,这两个方法都和对象锁有关,有锁的地方,必有synchronized。


    由于时间有限,在写博文的过程中参考过一些文献,在此表示感谢;同时鉴于水平原因,你难免有不足之处,欢迎斧正!


                          

本类排行

今日推荐

热门手游