想要了解更多关于Java生产者消费者问题的演变吗?那就看看这篇文章吧,我们分别用旧方法和新方法来处理这个问题。
生产者消费者问题是一个典型的多进程同步问题。
对于大多数人来说,这个问题可能是我们在学校,执行第一次并行算法所遇到的第一个同步问题。
虽然它很简单,但一直是并行计算中的最大挑战——多个进程共享一个资源。
<强>问题陈述强>
生产者和消费者两个程序,共享一个大小有限的公共缓冲区。
假设一个生产者”生产”一份数据并将其存储在缓冲区中,而一个消费者“消费”这份数据,并将这份数据从缓冲区中删除。
再假设现在这两个程序在并发地运行,我们需要确保当缓冲区的数据已满时,生产者不会放置新数据进的来,也要确保当缓冲区的数据为空时,消费者不会试图删除数据缓冲区的数据。
<>强解决方案强>
为了解决上述的并发问题,生产者和消费者将不得不相互通信。
如果缓冲区已满,生产者将处于睡眠状态,直到有通知信息唤醒。
在消费者将一些数据从缓冲区删除后,消费者将通知生产者,随后生产者将重新开始填充数据到缓冲区中。
如果缓冲区内容为空的化,那么情况是一样的,只不过,消费者会先等待生产者的通知。
但如果这种沟通做得不恰当,在进程彼此等待的位置可能导致程序死锁。
<强>经典的方法强>
首先来看一个典型的Java方案来解决这个问题。
包ProducerConsumer; 进口java.util.LinkedList; 进口java.util.Queue; 公开课ClassicProducerConsumerExample { 公共静态void main (String [] args)抛出InterruptedException { 缓冲缓冲区=new缓冲区(2); 线程producerThread=新线程(新Runnable () { @Override 公共空间run () { 尝试{ buffer.produce (); }捕捉(InterruptedException e) { e.printStackTrace (); } } }); 线程consumerThread=新线程(新Runnable () { @Override 公共空间run () { 尝试{ buffer.consume (); }捕捉(InterruptedException e) { e.printStackTrace (); } } }); producerThread.start (); consumerThread.start (); producerThread.join (); consumerThread.join (); } 静态类缓冲{ 私人Queue列表; 私人int大小; 公共缓冲区大小(int) { 这一点。,列表=new LinkedList<的在(); 这一点。大?大小; } 公共空间产生()抛出InterruptedException { int值=https://www.yisu.com/zixun/0; 而(真){ 同步(){ 而(list.size()>=大小){//等待消费者 wait (); } list.add(价值); system . out。println(“生产”+价值); 值+ +;//通知消费者 notify (); thread . sleep (1000); } } } 公共空间使用()抛出InterruptedException { 而(真){ 同步(){ 而(list.size ()==0) {//等待生产者 wait (); } int value=https://www.yisu.com/zixun/list.poll (); system . out。println(“消费”+价值);//通知生产商 notify (); thread . sleep (1000); } } } } }
这里我们有生产者和消费者两个线程,它们共享一个公共缓冲区。生产者线程开始产生新的元素并将它们存储在缓冲区。如果缓冲区已满,那么生产者线程进入睡眠状态,直到有通知唤醒。否,则生产者线程将会在缓冲区创建一个新元素然后通知消费者。就像我之前说的,这个过程也适用于消费者。如果缓冲区为空,那么消费者将等待生产者的通知。否,则消费者将从缓冲区删除一个元素并通知生产者。
正如你所看到的,在之前的例子中,生产者和消费者的工作都是管理缓冲区的对象。这些线程仅仅调用了buffer.produce()和buffer.consume()两个方法就搞定了一切。
对于缓冲区是否应该负责创建或者删除元素,一直都是一个有争议的话题,但在我看来,缓冲区不应该做这种事情。当然,这取决于你想要达到的目的,但在这种情况下,缓冲区应该只是负责以线程安全的形式存储合并元素,而不是生产新的元素。