在 Java 中使用线程的生产者-消费者解决方案
在计算中,生产者-消费者问题(也称为有界缓冲区问题)是多进程同步问题的经典例子。这个问题描述了两个进程,生产者和消费者,它们共享一个公共的、固定大小的缓冲区作为队列。
- 生产者的工作是生成数据,将其放入缓冲区,然后重新开始。
- 与此同时,消费者正在消耗数据(即从缓冲区中删除数据),一次一个。
问题 确保生产者不会试图在缓冲区已满时向缓冲区添加数据,消费者不会试图从空缓冲区移除数据。
解决方案 如果缓冲区已满,生产者要么进入睡眠状态,要么丢弃数据。下一次消费者从缓冲区中移除一个项目时,它会通知生产者,生产者会再次开始填充缓冲区。同样,如果消费者发现缓冲区是空的,他可以进入睡眠状态。生产者下一次将数据放入缓冲区时,会唤醒沉睡的消费者。 不充分的解决方案可能会导致死锁,两个进程都在等待被唤醒。 推荐阅读-JAVA 多线程JAVA 同步线程间通信
生产者消费者类的实现
- 一个链接列表–存储队列中的作业列表。
- 可变容量–检查列表是否已满
- 一种控制从列表中插入和提取的机制,这样,如果列表已满,我们就不会插入,如果列表为空,我们就不会从列表中删除。
注意:建议在离线 IDE 上测试以下程序,因为无限循环和休眠方法可能会导致其在任何在线 IDE 上超时
Java 语言(一种计算机语言,尤用于创建网站)
// Java program to implement solution of producer
// consumer problem.
import java.util.LinkedList;
public class Threadexample {
public static void main(String[] args)
throws InterruptedException
{
// Object of a class that has both produce()
// and consume() methods
final PC pc = new PC();
// Create producer thread
Thread t1 = new Thread(new Runnable() {
@Override
public void run()
{
try {
pc.produce();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// Create consumer thread
Thread t2 = new Thread(new Runnable() {
@Override
public void run()
{
try {
pc.consume();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// Start both threads
t1.start();
t2.start();
// t1 finishes before t2
t1.join();
t2.join();
}
// This class has a list, producer (adds items to list
// and consumer (removes items).
public static class PC {
// Create a list shared by producer and consumer
// Size of list is 2.
LinkedList<Integer> list = new LinkedList<>();
int capacity = 2;
// Function called by producer thread
public void produce() throws InterruptedException
{
int value = 0;
while (true) {
synchronized (this)
{
// producer thread waits while list
// is full
while (list.size() == capacity)
wait();
System.out.println("Producer produced-"
+ value);
// to insert the jobs in the list
list.add(value++);
// notifies the consumer thread that
// now it can start consuming
notify();
// makes the working of program easier
// to understand
Thread.sleep(1000);
}
}
}
// Function called by consumer thread
public void consume() throws InterruptedException
{
while (true) {
synchronized (this)
{
// consumer thread waits while list
// is empty
while (list.size() == 0)
wait();
// to retrieve the ifrst job in the list
int val = list.removeFirst();
System.out.println("Consumer consumed-"
+ val);
// Wake up producer thread
notify();
// and sleep
Thread.sleep(1000);
}
}
}
}
}
输出:
Producer produced-0
Producer produced-1
Consumer consumed-0
Consumer consumed-1
Producer produced-2
要点
- 在 PC 类(一个同时具有生产和消费方法的类)中,添加了一个作业的链接列表和列表的容量,以检查如果列表已满,生产者是否不生产。
- 在生产者类中,该值被初始化为 0。
- 此外,我们有一个无限的外部循环来插入列表中的值。在这个循环中,我们有一个同步块,这样一次只有一个生产者或消费者线程运行。
- 在将作业添加到列表之前,会有一个内部循环来检查作业列表是否已满,生产者线程会放弃 PC 上的固有锁并进入等待状态。
- 如果列表为空,控件将传递到循环下方,并在列表中添加一个值。
- 在消费者类中,我们再次有一个无限循环来从列表中提取一个值。
- 在内部,我们还有一个检查列表是否为空的内部循环。
- 如果它是空的,那么我们让消费者线程放弃 PC 上的锁,并将控制权交给生产者线程,以产生更多的作业。
- 如果列表不是空的,我们循环并从列表中移除一个项目。
- 在这两种方法中,我们都在所有语句的末尾使用 notify。原因很简单,一旦你有东西在列表中,你可以让消费线程消费它,或者如果你消费了东西,你可以让生产者生产一些东西。
- 两种方法末尾的 sleep()只是让程序的输出以分步的方式运行,而不是一次显示所有内容,这样您就可以看到程序中实际发生的事情。
运动:
- 建议读者使用 if 条件代替内环来检查边界条件。
- 试着让你的程序生产一个项目,然后在生产者生产任何其他项目之前让消费者立即消费它。
参考–https://en . Wikipedia . org/wiki/Productor % E2 % 80% 93 consumer _ problem
本文由里沙布·马赫塞供稿。如果你喜欢 GeeksforGeeks 并想投稿,你也可以使用write.geeksforgeeks.org写一篇文章或者把你的文章邮寄到 review-team@geeksforgeeks.org。看到你的文章出现在极客博客主页上,帮助其他极客。 如果你发现任何不正确的地方,或者你想分享更多关于上面讨论的话题的信息,请写评论。
版权属于:月萌API www.moonapi.com,转载请注明出处