【java】多线程 生产者消费者问题 共享资源区的大小 实例

本文通过一个实例介绍了生产者消费者问题的解决思路,使用Java实现了一个简单的模型,包括生产者类、消费者类和奶箱类。生产者将牛奶放入奶箱,消费者从奶箱中取出牛奶,通过共享资源奶箱来协调两者,同时考虑了资源的限制,如奶箱的容量。在多线程环境下,生产者和消费者通过wait()和notifyAll()方法实现同步。

生产者消费者问题包含三个成分:

1.生产者,用于提供数据   生产者类

2.消费者,用于拿走数据   消费者类

3.共享资源,就像是一个仓库,消费者把提供的数据放进去,生产者拿走数据。(用于解耦生产者和消费者的关系) 奶箱类

以下是一个实例

我们想喝牛奶但是又不想去商店买(生产者是商店,消费者是我,路径是 生产者->消费者),因此我们就选择了订奶

送奶工早上将牛奶从商店送来我手里,但是我早上又起不来那么早,难道就无法拿到牛奶了吗?

不,送奶工会将牛奶送到我家门口的奶箱,等我起床后只需要去奶箱里面拿就好了,这个过程我与送奶工并没有直接的见面(生产者与消费者没有变,但是我们两个中间多了一个中介:送奶工与奶箱,这时的路径就是:生产者->传递数据(送奶工)->共享资源(奶箱)->拿走数据(我)->消费者

而在我们的代码编写过程中,生产者->传递数据->共享资源->拿走数据->消费者,我们这样建立:

生产者类 实现Runnable接口,重写run()方法,且传入奶箱类对象【一条传入数据的路径】

消费者类 实现Runnable接口,重写run()方法,且传入奶箱类对象【一条拿走数据的路径】

奶箱类 包含表示奶箱大小,奶箱状态等的成员变量,及传入数据的方法和拿走数据的方法

测试类 建立对象并使用多线程实现

代码如下:(这里共享资源默认大小为一,只有放入与不放入) 

//奶箱类
public class Box {

	private int milk;
	private boolean state=false;////定义一个成员变量表示奶箱的状态,false表示没有牛奶
	
//下面使用的存放方式是默认奶箱容量为一
	public synchronized void put(int milk) {
		
		//如果有牛奶,等待消费
		if(state) {
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
		
		//如果没有牛奶,生产牛奶
		this.milk=milk;
		System.out.println("送奶工正在送第"+milk+"瓶奶");
		//生产完毕之后修改奶箱
	    state=true;
	    notifyAll();
	}
	
	public synchronized void get() {
		/*“线程操作的wait()、notify()、notifyAll()方法只能在同步控制方法或同步控制块内调用。
		 * 如果在非同步控制方法或控制块里调用,程序能通过编译
		 * 但运行的时候,将得到 IllegalMonitorStateException 异常,并伴随着一些含糊信息,比如 ‘当前线程不是拥有者’。
		 * 其实异常的含义是 调用wait()、notify()、notifyAll()的任务在调用这些方法前必须 ‘拥有’(获取)对象的锁。*/
		//如果有牛奶,等待拿走
		if(!state) {
			//如果没有牛奶,等待生产
		try {
				wait();
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
		}
		
		//如果有牛奶,消费牛奶
		System.out.println("用户拿走第"+milk+"瓶奶");
		state=false;//牛奶被拿走
		notifyAll();
	}
	
}

//生产者类
public class Producer implements Runnable {

	private Box box;

	public Producer(Box box) {
		
		this.box=box;
		
	}

	public void run() {
		
		for(int i=1;i<=5;i++) {//通过这里定义放入奶的数量
			box.put(i);
		}
		
	}

}

//消费者类
public class Customer implements Runnable {
	
    private Box box;

	public Customer(Box box) {
		
		this.box=box;
		
	}

	public void run() {
		while(true) {//这里用无限循环的方式是因为奶箱类里面定义的get()方法含有判断
			box.get();
		} 
	}
//测试类
public class BoxDemo {

	public static void main(String[] args) {
		
		Box box=new Box();//共享数据对象
		
		Producer producer=new Producer(box);//生产者对象,使用box做参数
		Customer customer=new Customer(box);//消费者对象,使用box做参数
		
		//创建两个线程
		Thread t1=new Thread(producer);
		Thread t2=new Thread(customer);
		
		t1.start();
		t2.start();

	}

}

再对比一下下面的代码,多定义了一个size表示奶箱的大小

public class Box {

	private int milk=9;
	static final int size=3;//奶箱一次最多放入三瓶奶
	static int state=0;////定义一个成员变量表示奶箱中的奶数
	
	public synchronized void put() {
		
		//如果牛奶数等于奶箱容积,等待消费
		if(state==size) {

			try {
				wait();
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
			
		}
		
		for(;state<size&&state>=0;state++) {
			
		//如果奶箱中的牛奶数小于奶箱容积,生产者继续生产并放入,每放入一瓶奶箱中的奶数量加一,直到奶数达到容积,停止放入
		System.out.println("送奶工正在送第"+milk+"瓶奶");
		milk--;
	    
	    }
		
	    notifyAll();
	    
	}
	
	public synchronized void get() {
		/*“线程操作的wait()、notify()、notifyAll()方法只能在同步控制方法或同步控制块内调用。
		 * 如果在非同步控制方法或控制块里调用,程序能通过编译
		 * 但运行的时候,将得到 IllegalMonitorStateException 异常,并伴随着一些含糊信息,比如 ‘当前线程不是拥有者’。
		 * 其实异常的含义是 调用wait()、notify()、notifyAll()的任务在调用这些方法前必须 ‘拥有’(获取)对象的锁。*/
		
		for(;state>0&&state<=size;state--) {//如果有牛奶,消费牛奶
			
			System.out.println("用户拿走奶箱中的第"+state+"瓶奶");
			
		}
		
		notifyAll();
		
		if(state==0) {
					//如果没有牛奶,等待生产
		            try {
						wait();
					} catch (InterruptedException e) {
						// TODO 自动生成的 catch 块
						e.printStackTrace();
					}
		            
		}

	}
	
}


public class Customer implements Runnable {
	
    private Box box;

	public Customer(Box box) {
		
		this.box=box;
		
	}

	public void run() {
		
		for(int i=1;i<3;i++) {//这里的i实际上是milk数除以size数
		//这个程序并不完善,没有判断当milk不能整除size时,送奶工如何送奶及用户如何拿奶
	    //这时还需要在put方法里面加一个判断
		//if(milk/size!=0)
		//size=milk%size;
			box.get();
         }
	}

}


public class Producer implements Runnable {

	private Box box;

	public Producer(Box box) {
		
		this.box=box;
		
	}

	public void run() {
		while(true) {
			box.put();
		}
		}
		

}


public class BoxDemo {

	public static void main(String[] args) {
		
		Box box=new Box();//共享数据对象
		
		Producer producer=new Producer(box);//生产者对象,使用box做参数
		Customer customer=new Customer(box);//消费者对象,使用box做参数
		
		//创建两个线程
		Thread t1=new Thread(producer);
		Thread t2=new Thread(customer);
		
		t1.start();
		t2.start();

	}

}

结果如下:

送奶工一次送三瓶奶,即共享资源大小为三 

送奶工正在送第9瓶奶
送奶工正在送第8瓶奶
送奶工正在送第7瓶奶
用户拿走奶箱中的第3瓶奶
用户拿走奶箱中的第2瓶奶
用户拿走奶箱中的第1瓶奶
送奶工正在送第6瓶奶
送奶工正在送第5瓶奶
送奶工正在送第4瓶奶
用户拿走奶箱中的第3瓶奶
用户拿走奶箱中的第2瓶奶
用户拿走奶箱中的第1瓶奶
送奶工正在送第3瓶奶
送奶工正在送第2瓶奶
送奶工正在送第1瓶奶

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老坛酸菜吃鸭子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值