java并发编程面试题2.生产者消费者模式

  • 面试题:生产者消费者模式
    写一个固定容量的同步容器,拥有put和get方法,以及getCount方法
    能够支持两个生产者线程以及十个消费者线程的阻塞调用

方法一:使用wait和notify来调用

public class MyContainer2<T> {
	final private LinkedList<T> lists = new LinkedList<>();
	final private int MAX = 10;
	private int count = 0;
	
	public synchronized void put(T t) {
		/*
		 * 解释下面的while
		 * 如果while换成if的话,假如if后面的条件为真,this.wait执行,交出锁,别的线程(消费者线程)消费后唤醒put之前,可能唤醒了另一个put,而这个put将count加到了最大
		 * 此时最初的put被唤醒,从this.wait后面开始执行,如果是if的话,会直接执行lists.add(t),而此时lists.size()是满的,所以会报异常
		 * 而使用while就不会出现这种情况
		 */
		while(lists.size() == MAX) {//陷阱1:为什么用while
			try {
				this.wait();//effect java中:wait大多数情况和while结合在一起用
			} catch (InterruptedException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
		lists.add(t);
		++count;
		this.notifyAll();//通知消费者线程进行消费
						 //为什么用notifyAll而不是notify??
		/*
		 *如果用notify的话,叫醒的只是一个线程,而叫醒的这个线程可能又是一个生产者 
		 *而生产者继续循环后会继续wait,这是程序执行不动了
		 */
	}
	
	public synchronized T get() {
		T t = null;
		while(lists.size() == 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
		
		t = lists.removeFirst();
		--count;
		this.notify();
		return t;
	}
	
	public static void main(String[] args) {
		MyContainer2<String> container1 = new MyContainer2<>();
		
		for (int i = 0; i < 10; i++) {
			
			new Thread(() -> {
				for (int j = 0; j < 5; j++) {
					System.out.println(container1.get());
				}
			}, "c" + i).start();
			
		}
		
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
		for (int i = 0; i < 2; i++) {
			new Thread(() -> {
				for (int j = 0; j < 25; j++) {
					container1.put(Thread.currentThread().getName() + "   " + j);
				}
			}, "p" + i).start();
		}
	}
}

方法二:使用lock和condition来实现

  • 对比两种方式,condition可以更加精确地指定那些线程会被唤醒
public class MyContainer2<T> {

	
	final private LinkedList<T> lists = new LinkedList<>();
	
	final private int MAX = 10;
	private int count = 0;
	
	private Lock lock = new ReentrantLock();
	private Condition producer =  lock.newCondition();
	private Condition consumer = lock.newCondition();
	
	public void put(T t) {
		try {
			lock.lock();
			while(lists.size() == MAX) {
				producer.await();
			}
			lists.add(t);
			++count;
			consumer.signalAll();//可以精确通知消费者线程被叫醒
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
	
	public T get() {
		T t = null;
		try {
			lock.lock();
			while(lists.size() == 0) {
				consumer.await();
			}
			t = lists.removeFirst();
			count--;
			producer.signalAll(); // 可以精确通知生产者线程被叫醒
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
		return t;
	}
	
	public static void main(String[] args) {
		MyContainer2<String> container1 = new MyContainer2<>();
		
		for (int i = 0; i < 10; i++) {
			
			new Thread(() -> {
				for (int j = 0; j < 5; j++) {
					System.out.println(container1.get());
				}
			}, "c" + i).start();
			
		}
		
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		
		for (int i = 0; i < 2; i++) {
			new Thread(() -> {
				for (int j = 0; j < 25; j++) {
					container1.put(Thread.currentThread().getName() + "   " + j);
				}
			}, "p" + i).start();
		}
	}
}

你可能感兴趣的:(java_concurrent)