《多线程操作之生产者消费者》(单生产单消费&多生产多消费)

说明1:假设有一个放商品的盘子(此盘子只能放下一个商品)。生产者每次生产一个商品之后,放到这个盘子里,然后唤醒消费者来消费这个面包。消费者消费完这个商品之后,就唤醒生产者生产下一个商品。前提是,只有盘子里没有商品时,生产者才生产商品,只有盘子里有商品时,消费者才来消费。因此第一个程序是一个“单生产”  “单消费” 的问题。代码如下所示:

 

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//描述资源  
//资源属性:商品名称和编号 
//行为:对商品名称赋值,获取商品
class Resource{
	
	private String name;
	private int count = 1;

	// 定义标记
	private boolean flag = false; //初始标记为假,表明盘子里没有商品(面包)

	// 定义一个锁对象
	private Lock lock = new ReentrantLock();

	//获取锁上的Condition对象	
	private Condition producer = lock.newCondition();//负责生产
	private Condition consumer = lock.newCondition();//负责消费 

	// 提供生产商品的方法
	public void set(String name) {
		lock.lock(); //获取锁
		try {
			while (flag) //当flag标记为真时,说明盘子里有商品(面包)此时生产者等待,否则,生产商品
				try {
					producer.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			this.name = name + count;
			count++;
			System.out.println(Thread.currentThread().getName() + "...生产者..." + this.name);
			flag = true;
			// 唤醒一个消费者
			consumer.signal();
		} finally {//释放锁
			lock.unlock();
		}
	}

	//提供消费的方法
	public void out() {
		lock.lock(); //获取锁
		try {
			while (!flag) //当flag标记为假时,说明盘子里没有商品(面包),此时,消费者等待,否则,消费商品(面包)
				try {
					consumer.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			System.out.println(Thread.currentThread().getName() + "...消费者..." + this.name);
			flag = false;
			//唤醒一个生产者
			producer.signal();
		} finally {//释放锁
			lock.unlock();
		}
	}
}

// 描述生产者
class Producer implements Runnable {
	// 生产者一初始化就要有资源
	private Resource r;

	public Producer(Resource r) {
		this.r = r;
	}

	@Override
	public void run() {
		while (true) {
			r.set("面包");
		}
	}
}

// 描述消费者
class Consumer implements Runnable {
	// 消费者一初始化就要有资源
	private Resource r;

	public Consumer(Resource r) {
		this.r = r;
	}

	@Override
	public void run() {
		while (true) {
			r.out();
		}
	}
}

public class ProducerConsumer {
	public static void main(String[] args) {
		// 创建资源对象
		Resource r = new Resource();

		// 创建线程任务
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);

		// 创建线程对象(两个生产者,两个消费者)
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(pro);
		Thread t3 = new Thread(con);
		Thread t4 = new Thread(con);

		// 开启线程
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

 

  运行结果如下图所示:(注意Ctrl+C结束程序)

说明2:在多生产多消费问题中,我们假设有很多个盘子组成一个数组,生产者不停的生产商品(面包)往数组里面放,消费者不停的消费。当生产者判断已经所有盘子里都已经有面包时【注意,此处生产者判断所有盘子里都有面包,不是简单的判断生产的面包数目等于数组的长度这么简单,因为生产者在生产面包的同时,消费者也在消费面包,当生产者把生产的面包放到最后一个盘子里时,可能消费者已经消费了前面若干个面包了,所以此时并不满足所有盘子里都有面包。】生产者等待,唤醒一个消费者来消费。当消费者判断所有盘子里都没有面包时【注意:此处也不是简单的判断消费的面包数目等于数组长度这么简单,和上面的分析同理】消费者等待,唤醒一个生产者进行生产。多生产多消费的代码如下:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class BoundedBuffer {
   final Lock lock = new ReentrantLock();//锁
   final Condition notFull  = lock.newCondition(); //生产
   final Condition notEmpty = lock.newCondition(); //消费

   final Object[] items = new Object[100];//存储商品的容器。
   int putptr/*生产者使用的角标*/, takeptr/*消费者使用的角标*/, count/*计数器*/;

	/*生产者使用的方法,往数组中存储商品*/
   public void put(Object x) throws InterruptedException {
     lock.lock(); //获取锁
     try {
       while (count == items.length) //判断计数器是否已到数组长度。满了。
         notFull.await();//生产就等待。

       items[putptr] = x; //按照角标将商品存储到数组中
	   System.out.println(Thread.currentThread().getName()+"...生产者..."+items[putptr]+"--->"+count);
		
       if (++putptr == items.length) //如果存储的角标到了数组的长度,就将角标归零。
			putptr = 0;
       ++count;//计数器自增。
       notEmpty.signal();//唤醒一个消费者
     } finally {
       lock.unlock();
     }
   }

	//消费者使用的方法
   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) //如果计数器为0,说明没有商品,消费者等待。
         notEmpty.await();
       Object x = items[takeptr]; //从数组中通过消费者角标获取商品。

       if (++takeptr == items.length) //如果消费的角标等于了数组的长度,将角标归零。
		   takeptr = 0;
       --count;//计数器自减。
	   System.out.println(Thread.currentThread().getName()+"...消费者--->"+items[takeptr]+"..."+count);
       notFull.signal();//唤醒生产者。
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }
 
//生产者
class Producer implements Runnable 
{
	//private int n = 1;
	private BoundedBuffer b;
	public Producer(BoundedBuffer b){
		this.b = b;
	}

	public void run(){
		while(true){
			try{
				b.put("面包");
				//n++;
			}catch(InterruptedException e){}
			
		}
	}
}

//消费者
class Consumer implements Runnable 
{
	private BoundedBuffer b;
	public Consumer(BoundedBuffer b){
		this.b = b;
	}

	public void run(){
		while(true){
			try{
				b.take();
			}catch(InterruptedException e){}
			
		}
	}
}

//主函数
public class ThreadDemo12
{
	public static void main(String args[]){
		//创建资源对象
		BoundedBuffer b = new BoundedBuffer();

		//创建线程任务
		Producer pro = new Producer(b);
		Consumer con = new Consumer(b);

		//创建线程对象
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(pro);
		Thread t3 = new Thread(con);
		Thread t4 = new Thread(con);

		//开启线程
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

  运行截图如下:

欢迎留言交流!

 

转载于:https://www.cnblogs.com/sun-/p/10307878.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、实验目的 (1)掌握基本的同步互斥算法,理解生产者消费者同步的问题模型。 (2)了解Windows 2000/XP中多线程的并发执行机制,线程间的同步和互斥。 (3)学习使用Windows2000/XP中基本的同步对象,掌握相应的API。 2、实验要求 (1)创建生产者消费者线程 在Windows2000环境下,创建一个控制台进程,在此进程中创建n个线程来模拟生产者或者消费者。这些线程的信息由本程序定义的“测试用例文件”中予以指定。 该文件的格式和含义如下: 3 1 P 3 2 P 4 3 C 4 1 4 P 2 5 C 3 1 2 4 第一行说明程序中设置几个临界区,其余每行分别描述了一个生产者或者消费者线程的信息。每一行的各字段间用Tab键隔开。不管是消费者还是生产者,都有一个对应的线程号,即每一行开始字段那个整数。第二个字段用字母P或者C区分是生产者还是消费者。第三个字段表示在进入相应线程后,在进行生产消费动作前的休眠时间,以秒计时;这样做的目的是可以通过调整这一列参数,控制开始进行生产消费动作的时间。如果是代表生产者,则该行只有三个字段。如果代表消费者,则该行后边还有若干字段,代表要求消费的产品所对应的生产者的线程号。所以务必确认这些对应的线程号存在并且该线程代表一个生产者。 (2)生产消费的规则 在按照上述要求创建线程进行相应的读写操作时,还需要符合以下要求: ①共享缓冲区存在空闲空间时,生产者即可使用共享缓冲区。 ②从上边的测试数据文件例子可以看出,某一生产者生产一个产品后,可能不止一个消费者,或者一个消费者多次地请求消费该产品。此时,只有当所有的消费需求都被满足以后,该产品所在的共享缓冲区才可以被释放,并作为空闲空间允许新的生产者使用。 ③每个消费者线程的各个消费需求之间存在先后顺序。例如上述测试用例文件包含一行信息“5 C 3 l 2 4”,可知这代表一个消费者线程,该线程请求消费1,2,4号生产者线程生产的产品。而这种消费是有严格顺序的,消费1号线程产品的请求得到满足后才能继续往下请求2号生产者线程的产品。 ④要求在每个线程发出读写操作申请、开始读写操作和结束读写操作时分别显示提示信息。 (3)相关基础知识 本实验所使用的生产者消费者模型具有如下特点: 本实验的多个缓冲区不是环形循环的,也不要求按顺序访问。生产者可以把产品放到目前某一个空缓冲区中。 消费者消费指定生产者的产品。 在测试用例文件中指定了所有的生产消费的需求,只有当共享缓冲区的数据满足了所有关于它的消费需求后,此共享缓冲区才可以作为空闲空间允许新的生产者使用。 本实验在为生产者分配缓冲区时各生产者间必须互斥,此后各个生产者的具体生产活动可以并发。而消费者之间只有在对同一产品进行消费时才需要互斥,同时它们在消费过程结束时需要判断该消费对象是否已经消费完毕并清除该产品。 Windows用来实现同步和互斥的实体。在Windows中,常见的同步对象有:信号量(Semaphore)、互斥量(Mutex)、临界段(CriticalSection)等。使用这些对象都分为三个步骤,一是创建或者初始化:接着请求该同步对象,随即进入临界区,这一步对应于互斥量的上锁;最后释放该同步对象,这对应于互斥量的解锁。这些同步对象在一个线程中创建,在其他线程中都可以使用,从而实现同步互斥。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值