生产者消费者模型的实现(Java版)

前言:

生产者消费者模型是一个很常见的面试题,因为很多问题都可以转化为这个模型。

首先来理解下什么是生产者消费者模型:生产者消费者模型可以理解为一个餐厅中的活动。首先厨师会将菜品放在桌子上,顾客开始消耗菜品。如果桌子满了,厨师会暂停上菜,等到桌子有空位再继续上菜;同理,当桌子上的菜品消耗完了之后顾客也会停止消耗,等待上菜。这就是一个经典的生产者消费者模型。

wait()方法和notify()方法

我将会使用wait()方法和notify()方法来实现生产者消费者模型,我们先了解下wait()方法和notify()方法:

wait()方法:

查看API文档,wait()方法有三个:

public final void wait() throws InterruptedException

//使当前线程等待,直到其他线程调用notify()方法或notifyAll()方法时恢复,该方法和wait(0)方法执行的效果一样。
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0). 

public final void wait(long timeout) throws InterruptedException

//使当前线程等待,直到其他线程调用notify()方法或notifyAll()方法,或者当前线程等待时间超过timeout时恢复(注意:timeout为0时不考虑线程等待时间,只能在别的线程调用notify()或notifyAll()方法时恢复)
Causes the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed. 

public final void wait(long timeout, int nanos) throws InterruptedException

//和wait(long timeout)相似,只是这个方法可以精确到纳秒来控制线程等待的时间,线程等待时间为(timeout*1000000(纳秒)+nanos)纳秒。wait(0,0)和wait(0)方法效果一样。
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed. 
This method is similar to the wait method of one argument, but it allows finer control over the amount of time to wait for a notification before giving up. The amount of real time, measured in nanoseconds, is given by: 
 1000000*timeout+nanos
In all other respects, this method does the same thing as the method wait(long) of one argument. In particular, wait(0, 0) means the same thing as wait(0). 

notify()方法和notifyAll()方法:

public final void notify()

//唤醒在此对象监视器中等待的单个线程。如果当前对象中有多个线程在等待,那么唤醒的线程是随机的其中一个。
Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the wait methods. 

public final void notifyAll()

//唤醒在此对象监视器中等待的所有线程。
Wakes up all threads that are waiting on this object's monitor. A thread waits on an object's monitor by calling one of the wait methods. 

wait()方法和notifyAll()方法实现生产者消费者模型

下面来实现一个较为简单的生产者消费者模型:

public class ProducerConsumer {
	public static void main(String[] args) {
		Container c = new Container();
		Producer p = new Producer(c);
		Consumer co = new Consumer(c);
		Consumer co1 = new Consumer(c);
		Thread r1 = new Thread(p);
		Thread r2 = new Thread(co);
		Thread r3 = new Thread(co1);
		r1.start();
		r2.start();
		r3.start();
	}
	
	static class Food {
		int num;
		Food (int i) {
			this.num = i;
		}
		
		public String toString() {
			return "food: " + (num+1);//显示第几号食物
		}
	}
	
	static class Container {	//容器对象
		int index = 0;
		Food[] f = new Food[30];
		
		//写生产和消费的方法的时候,需要考虑并发的情况。
		//为了防止多个线程同时生产或者消费可能导致的数据丢失,应在生产和消费的方法上加锁。
		public synchronized void add(Food a) { 
			while (index >= f.length) {
				try{
					System.out.println("The Container is full!");
					this.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.print("Adding...  ");
			f[index] = a;
			index++;
			notifyAll();//唤醒其他消费者线程
			System.out.println("Thread " +Thread.currentThread().getName()
					+ "  生产者 生产:" + a.toString());
		}
		
		public synchronized Food take() {
			while (index == 0) {
				try{
					System.out.println("The Container is empty!");
					this.wait();
				} catch (InterruptedException e){
					e.printStackTrace();
				}
			}
			System.out.print("Taking...  ");
			index --;
			notifyAll();//唤醒生产者线程
			System.out.println("Thread " +Thread.currentThread().getName() 
					+ "  消费者 消费:" + f[index].toString());
			return f[index];//返回被消费的Food对象
		}
	}
	
	static class Producer implements Runnable {
		Container s = null;
		Producer(Container a) {
			this.s = a;
		}

		public void run() {
			// TODO Auto-generated method stub
			for (int i = 0; i < 100; i++) {
				Food b = new Food(i);//为生产的Food对象添加序号信息
				s.add(b);
			}
		}
	}
	static class Consumer implements Runnable {
		Container s = null;
		Consumer(Container a) { //引用容器的对象
			this.s = a;
		}
		public void run() {
			// TODO Auto-generated method stub
			for (int i = 0; i < 40; i++) {
				s.take();
			}
		}
	}
}

注:和wait()方法有些类似的有一个Thread.sleep()方法。

public static void sleep(long millis) throws InterruptedException

//使当前线程休眠millis毫秒后恢复运行
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. The thread does not lose ownership of any monitors.

public static void sleep(long millis, int nanos) throws InterruptedException

//使当前线程休眠(millis*1000000 + nanos)纳秒后恢复运行
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds plus the specified number of nanoseconds, subject to the precision and accuracy of system timers and schedulers. The thread does not lose ownership of any monitors.

但是这个例子里不能用Thread.sleep()方法,这也是Thread.sleep()方法和wait()方法的区别:

线程调用wait()方法之后会释放锁,这时别的线程就可以调用synchronized的方法;

线程调用Thread.sleep()方法之后,线程在睡眠的同时是不会释放锁的,这时候如果别的线程也需要锁就会导致阻塞。

发布了12 篇原创文章 · 获赞 0 · 访问量 298

猜你喜欢

转载自blog.csdn.net/qq_39790633/article/details/103543110
今日推荐