生产者消费者问题是研究多线程程序时绕不开的经典问题之一。
问题描述如下。使用一个商品的缓存池用来存放商品。当池子满时,生产者不能往池子里加入商品;当池子空时,消费者不能从池子中取得商品。
使用Object的方法 wait() notify()/notifyAll()实现
获取锁和释放锁都是针对Object而言的,而和线程无关。试想如果和线程相关,那么一个线程就无法使用多个锁。
import java.util.concurrent.Executor; import java.util.concurrent.Executors; public class Pool { private int MAX; private int cnt = 0; public Pool(int MAX) { this.MAX = MAX; } public synchronized void produce() throws InterruptedException { while (cnt == MAX) { wait(); } System.out.println("Produce one.. Now:" + ++cnt); notify(); } public synchronized void consume() throws InterruptedException { while (cnt == 0) { wait(); } System.out.println("Consume one.. Now:" + --cnt); notifyAll(); } public static void main(String[] args) { Pool pool = new Pool(6); Executor executor = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { executor.execute(() -> { try { pool.produce(); } catch (InterruptedException e) { e.printStackTrace(); } }); } for (int i = 0; i < 10; i++) { executor.execute(() -> { try { pool.consume(); } catch (InterruptedException e) { e.printStackTrace(); } }); } } }
使用 wait() notify()/notifyAll()的缺点在于在生产者唤醒消费者或者消费者唤醒生产者时,由于生产者和消费者使用同一个锁,所以生产者也会将生产者唤醒,消费者也会将消费者唤醒。(这一点也被字节跳动的面试官问到啦TAT)
Condition可以指定多个等待的条件,因此使用Condition可以解决这一问题。
使用ReentrantLock和Condition的await() signal()/signalAll()实现
import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class Pool { private int MAX; private int cnt = 0; private ReentrantLock lock = new ReentrantLock(); private Condition produce = lock.newCondition(); private Condition consume = lock.newCondition(); public Pool(int MAX) { this.MAX = MAX; } public void produce() throws InterruptedException { lock.lock(); try { while (cnt == MAX) { produce.await(); } System.out.println("Produce one.. Now:" + ++cnt); consume.signal(); } finally { lock.unlock(); } } public void consume() throws InterruptedException { lock.lock(); try { while (cnt == 0) { consume.await(); } System.out.println("Consume one.. Now:" + --cnt); produce.signal(); } finally { lock.unlock(); } } public static void main(String[] args) { Pool pool = new Pool(6); Executor executor = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { executor.execute(() -> { try { pool.produce(); } catch (InterruptedException e) { e.printStackTrace(); } }); } for (int i = 0; i < 10; i++) { executor.execute(() -> { try { pool.consume(); } catch (InterruptedException e) { e.printStackTrace(); } }); } } }
原文:https://www.cnblogs.com/yfzhou/p/10996801.html