生产者消费者的while有点迷,感觉用if就足够了,请老师指点一下

来源:10-7 用条件对象实现生产者模式

Panda_io

2021-01-11

        private void produce() {
            while (true) {
                lock.lock();
                try {
                   //请问老师这里用while有什么特别的用意吗,我用if也是一样的效果
                   //由于只有一把锁,也就表明生产者和消费者只有一个在同一时间运行
                   //所以任何时候被唤醒,队列里的数量肯定不等于queueSzie
                   //因为消费者消费之后才会唤醒生产者,所以不会进入循环第二次
                   //这里用if不是也可以吗?有点疑惑肯定老师指点一下
                    while (queue.size() == queueSize) {
                        System.out.println("队列满,等待有空余");
                        try {
                            notFull.await();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    queue.offer(1);
                    notEmpty.signalAll();
                    System.out.println("向队列插入了一个元素,队列剩余空间" + (queueSize - queue.size()));
                }finally {
                    lock.unlock();
                }
            }
        }

从输出结果来看,持有锁的线程释放锁再立马获取锁,ReentrantLock的默认非公平策略插队现象真的非常明显,节省了切换线程的开销,大部分情况都是塞满直到阻塞才释放这把锁如果数量非常大的话感觉这种平衡能力不太明显呀老师
输出结果:
从队列里取走了一个数据,队列里剩余9个元素
从队列里取走了一个数据,队列里剩余8个元素
从队列里取走了一个数据,队列里剩余7个元素
从队列里取走了一个数据,队列里剩余6个元素
从队列里取走了一个数据,队列里剩余5个元素
从队列里取走了一个数据,队列里剩余4个元素
从队列里取走了一个数据,队列里剩余3个元素
从队列里取走了一个数据,队列里剩余2个元素
从队列里取走了一个数据,队列里剩余1个元素
从队列里取走了一个数据,队列里剩余0个元素
队列空,等待数据
向队列插入了一个元素,队列剩余空间9
向队列插入了一个元素,队列剩余空间8
向队列插入了一个元素,队列剩余空间7
向队列插入了一个元素,队列剩余空间6
向队列插入了一个元素,队列剩余空间5
向队列插入了一个元素,队列剩余空间4
向队列插入了一个元素,队列剩余空间3
向队列插入了一个元素,队列剩余空间2
向队列插入了一个元素,队列剩余空间1
向队列插入了一个元素,队列剩余空间0
队列满,等待有空余

老师在注释里有个问题哟,谢谢老师!

写回答

1回答

悟空

2021-01-11

我们在 take() 方法中使用 while( queue.size() == 0 ) 检查队列状态,而不能用 if( queue.size() == 0 )。为什么呢?大家思考这样一种情况,因为生产者消费者往往是多线程的,我们假设有两个消费者,第一个消费者线程获取数据时,发现队列为空,便进入等待状态;因为第一个线程在等待时会释放 Lock 锁,所以第二个消费者可以进入并执行 if( queue.size() == 0 ),也发现队列为空,于是第二个线程也进入等待;而此时,如果生产者生产了一个数据,便会唤醒两个消费者线程,而两个线程中只有一个线程可以拿到锁,并执行 queue.remove 操作,另外一个线程因为没有拿到锁而卡在被唤醒的地方,而第一个线程执行完操作后会在 finally 中通过 unlock 解锁,而此时第二个线程便可以拿到被第一个线程释放的锁,继续执行操作,也会去调用 queue.remove 操作,然而这个时候队列已经为空了,所以会抛出 NoSuchElementException 异常,这不符合我们的逻辑。而如果用 while 做检查,当第一个消费者被唤醒得到锁并移除数据之后,第二个线程在执行 remove 前仍会进行 while 检查,发现此时依然满足 queue.size() == 0 的条件,就会继续执行 await 方法,避免了获取的数据为 null 或抛出异常的情况。

0
1
Panda_io
哇老师解释得太好了,感谢悟空老师,真的非常感谢!
2021-01-12
共1条回复

深度解密Java并发工具,精通JUC,成为并发多面手

JUC全方位讲解,构建并发工具类知识体系

1599 学习 · 573 问题

查看课程