Java面試題-生產者和消費者-高併發
阿新 • • 發佈:2018-11-11
面試題:寫一個固定容量同步容器,擁有put和get方法,以及getCount方法,
能夠支援,2個生產者執行緒及10個消費者執行緒的阻塞呼叫(經常問!)
-----------------------------------------------------------------------------
1. 使用synchronized、wait和notify/notifyall來實現
package interview_test; /** * 使用wait和notify/notifyall來實現 * @author x1c * */ import java.util.LinkedList; import java.util.concurrent.TimeUnit; public class MyContainer1<T> { final private LinkedList<T> list = new LinkedList<>(); final private int MAX = 10; private int count = 0; public synchronized void put(T t) { while (list.size() == MAX) { //想想為什麼用while而不是if while會再次檢查! try { this.wait(); //effective java一書中說wait 99.9%和 while一起使用,wait釋放鎖 } catch (InterruptedException e) { e.printStackTrace(); } } list.add(t); ++count; this.notifyAll(); //通知消費者執行緒進行消費 } public synchronized T get() { T t = null; while (list.size() == 0) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } t = list.removeFirst(); //Removes and returns the first element from this list. count --; this.notifyAll(); return t; } public static void main(String[] args) { MyContainer1<String> c = new MyContainer1<>(); //啟動消費者執行緒 for (int i = 0; i < 10; i++) { //10個消費者執行緒 new Thread(()->{ for (int j = 0; j < 4; j++) { System.out.println(Thread.currentThread().getName()+"-->"+c.get()); } },"consumer"+i) .start(); } System.out.println("等待2秒生產-開始"); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("等待2秒生產-結束"); //啟動生產者執行緒 for (int i = 0; i < 2; i++) { //2個生產者執行緒 new Thread(()->{ for (int j = 0; j < 8 ;j++) { c.put(Thread.currentThread().getName()+" "+j ); System.out.println("生產者:"+Thread.currentThread().getName()+" "+j ); } },"product"+i) .start(); } } }
2. 使用Lock、await和signalAll
package interview_test; /** * 面試題:生產者和消費者 經常問 * 使用Lock和Condition來實現 *Condition方式可以更加精確的指定那些執行緒被喚醒 * @author x1c * 總結:synchronized wait notifyall一起使用 * lock await signalAll 一起使用 * */ import java.util.LinkedList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MyContainer2<T> { final private LinkedList<T> list = new LinkedList<>(); final private int MAX = 10; private int count = 0; private Lock lock = new ReentrantLock(); //Lock介面 java.util.concurrent.locks.Lock ReentrantLock重入鎖 private Condition producer = lock.newCondition(); //Condition介面 java.util.concurrent.locks.Condition private Condition consumer = lock.newCondition(); public /*synchronized*/ void put(T t) { lock.lock();// while (list.size() == MAX) { //想想為什麼用while而不是if while會再次檢查! try { //this.wait(); //effective java一書中說wait 99.9%和 while一起使用,wait釋放鎖 producer.await();// } catch (InterruptedException e) { e.printStackTrace(); } } list.add(t); ++count; // this.notifyAll(); //通知消費者執行緒進行消費 consumer.signalAll(); //精確叫醒消費者執行緒 } public /*synchronized*/ T get() { T t = null; lock.lock();// while (list.size() == 0) { try { // this.wait(); consumer.await();// } catch (InterruptedException e) { e.printStackTrace(); } } t = list.removeFirst(); //Removes and returns the first element from this list. count --; // this.notifyAll(); consumer.signalAll(); //精確指定消費者執行緒消費 return t; } public static void main(String[] args) { MyContainer2<String> c = new MyContainer2<>(); //啟動消費者執行緒 for (int i = 0; i < 10; i++) { //10個消費者執行緒 new Thread(()->{ for (int j = 0; j < 4; j++) { System.out.println(Thread.currentThread().getName()+"-->"+c.get()); } },"consumer"+i) .start(); } System.out.println("等待2秒生產-開始"); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("等待2秒生產-結束"); //啟動生產者執行緒 for (int i = 0; i < 2; i++) { //2個生產者執行緒 new Thread(()->{ for (int j = 0; j < 8 ;j++) { c.put(Thread.currentThread().getName()+" "+j ); System.out.println("生產者:"+Thread.currentThread().getName()+" "+j ); } },"product"+i) .start(); } } }
------------------------------------------EOF-----------------------------------------------------