java 实现生产者消费者模式的三种方法

很久以前使用基础同步的方式实现过一次。最近看到一篇文章使用多种方式进行实现,所以动手实践一下。

参考:https://zhuanlan.zhihu.com/p/20300609

 

关于生产者消费者问题、读者写者问题的最新研究,可以参考我的另一篇文章:

http://www.xie4ever.com/2017/08/06/java-%E5%AF%B9%E7%94%9F%E4%BA%A7%E8%80%85%E6%B6%88%E8%B4%B9%E8%80%85%E9%97%AE%E9%A2%98%E3%80%81%E8%AF%BB%E8%80%85%E5%86%99%E8%80%85%E9%97%AE%E9%A2%98%E7%9A%84%E7%AE%80%E5%8D%95%E7%A0%94%E7%A9%B6/

一、使用synchronized、wait、notifyAll

之前写过这种方案:

http://www.xie4ever.com/2017/03/15/java-%E5%9C%A8%E5%90%8C%E6%AD%A5%E4%BB%A3%E7%A0%81%E4%B8%AD%E4%BD%BF%E7%94%A8%E6%9D%A1%E4%BB%B6/

具体代码为:

如果这样写,生产和消费(线程)不会同时进行。

生产者将一次性生产10个数据,然后消费者把所有数据都消费完,如此反复,没有起到“同时执行”/“相互执行”的效果。

一开始我觉得这种现象很诡异。后来才发现,“同时执行”涉及到另外一个“读者与写者”问题,生产者消费者模式并不要求生产和消费“同时执行”。

(2)使用Lock

既然可以使用普通的同步方法,那使用Lock也是可以的。

test.java

感觉这种写法比直接使用同步方法效果要好(问题:好在哪里?)。但是依然不能“同时执行”。

(3)使用BlockingQueue

test.java

重要的是put()方法和take()方法。

如果队列满了,put()会阻塞当前生产者线程,停止生产数据。如果队列的数据被消费(不为满),那么该生产者线程会被唤醒,继续生产,直到队列被填满。

如果队列为空,take()会阻塞当前消费者线程,停止消费数据。如果队列的数据有新数据(不为空),那么该消费者线程会被唤醒,继续消费,直到队列为空。

个人认为,Juc提供BlockingQueue的目的,就是为了解决“生产者消费者”问题。我们不需要再手写同步方法,不需要加锁,不需要维护自己的队列,BlockingQueue全都帮我们做了。使用这种方式是最方便的。

(当然BlockingQueue的底层依然是使用Lock实现的)

二、总结

以上三种实现方式,都有“读者和写者”问题(生产和消费不会同时进行)。

下次我将单独研究这个问题。