在Java | Set 2中使用信号量的生产者-消费者解决方案

先决条件—— Java中的信号量 , 进程间通信 , 使用信号量的生产者-消费者问题|集1

null

在计算中,生产者-消费者问题(也称为有界缓冲区问题)是多进程同步问题的经典例子。这个问题描述了两个进程,生产者和消费者,它们共享一个通用的、固定大小的缓冲区,用作队列。

  • 生产者的工作是生成数据,将其放入缓冲区,然后重新开始。
  • 同时,消费者一次一块地消费数据(即从缓冲区中删除数据)。

问题: 确保生产者不会在缓冲区已满时尝试将数据添加到缓冲区中,消费者也不会尝试从空缓冲区中删除数据。

解决方案: 如果缓冲区已满,生产者要么进入睡眠状态,要么丢弃数据。下一次消费者从缓冲区中移除一个项目时,它会通知生产者,后者会再次开始填充缓冲区。同样,如果消费者发现缓冲区为空,也可以进入睡眠状态。下次生产者将数据放入缓冲区时,它会唤醒睡眠中的消费者。 解决方案不充分可能导致僵局 这两个过程都在等待被唤醒。

在岗 在Java中使用线程的生产者-消费者解决方案, 我们已经使用 线程间通信 (等待()、通知()、睡眠()。在本文中,我们将使用 信号灯 实现同样的目标。

以下解决方案由四类组成:

  1. Q :尝试同步的队列
  2. 制片人: 正在生成队列条目的线程对象
  3. 消费者: 正在使用队列条目的线程对象
  4. 个人电脑: 创建单个Q、生产者和消费者的驱动程序类。

// Java implementation of a producer and consumer
// that use semaphores to control synchronization.
import java.util.concurrent.Semaphore;
class Q {
// an item
int item;
// semCon initialized with 0 permits
// to ensure put() executes first
static Semaphore semCon = new Semaphore( 0 );
static Semaphore semProd = new Semaphore( 1 );
// to get an item from buffer
void get()
{
try {
// Before consumer can consume an item,
// it must acquire a permit from semCon
semCon.acquire();
}
catch (InterruptedException e) {
System.out.println( "InterruptedException caught" );
}
// consumer consuming an item
System.out.println( "Consumer consumed item : " + item);
// After consumer consumes the item,
// it releases semProd to notify producer
semProd.release();
}
// to put an item in buffer
void put( int item)
{
try {
// Before producer can produce an item,
// it must acquire a permit from semProd
semProd.acquire();
}
catch (InterruptedException e) {
System.out.println( "InterruptedException caught" );
}
// producer producing an item
this .item = item;
System.out.println( "Producer produced item : " + item);
// After producer produces the item,
// it releases semCon to notify consumer
semCon.release();
}
}
// Producer class
class Producer implements Runnable {
Q q;
Producer(Q q)
{
this .q = q;
new Thread( this , "Producer" ).start();
}
public void run()
{
for ( int i = 0 ; i < 5 ; i++)
// producer put items
q.put(i);
}
}
// Consumer class
class Consumer implements Runnable {
Q q;
Consumer(Q q)
{
this .q = q;
new Thread( this , "Consumer" ).start();
}
public void run()
{
for ( int i = 0 ; i < 5 ; i++)
// consumer get items
q.get();
}
}
// Driver class
class PC {
public static void main(String args[])
{
// creating buffer queue
Q q = new Q();
// starting consumer thread
new Consumer(q);
// starting producer thread
new Producer(q);
}
}


输出:

Producer produced item : 0
Consumer consumed item : 0
Producer produced item : 1
Consumer consumed item : 1
Producer produced item : 2
Consumer consumed item : 2
Producer produced item : 3
Consumer consumed item : 3
Producer produced item : 4
Consumer consumed item : 4

说明: 正如你所看到的,呼叫 put() 得到() 是同步的,即每次调用put()后都会调用get(),并且不会遗漏任何项。如果没有信号量,对put()的多个调用将在没有匹配对get()的调用的情况下发生,从而导致项目丢失。(为了证明这一点,请删除信号量代码并观察结果。)

put()和get()调用的顺序由两个信号量处理: 塞姆普罗德 塞姆肯 .

  • put()在生产物品之前,必须获得semProd的许可。制作完物品后,它会释放semCon。
  • get()必须获得semCon的许可才能使用物品。消费完物品后,它会释放semProd。
  • 这种“给予和索取”机制确保每次调用put()之后都必须调用get()。
  • 还要注意,semCon是在没有可用许可证的情况下初始化的。这确保put()首先执行。设置初始同步状态的能力是信号量更强大的方面之一。

本文由 高拉夫·米格拉尼 .如果你喜欢GeekSforgek,并想贡献自己的力量,你也可以使用 贡献极客。组织 或者把你的文章寄到contribute@geeksforgeeks.org.看到你的文章出现在Geeksforgeks主页上,并帮助其他极客。

如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享