先决条件—— Java中的信号量 , 进程间通信 , 使用信号量的生产者-消费者问题|集1
在计算中,生产者-消费者问题(也称为有界缓冲区问题)是多进程同步问题的经典例子。这个问题描述了两个进程,生产者和消费者,它们共享一个通用的、固定大小的缓冲区,用作队列。
- 生产者的工作是生成数据,将其放入缓冲区,然后重新开始。
- 同时,消费者一次一块地消费数据(即从缓冲区中删除数据)。
问题: 确保生产者不会在缓冲区已满时尝试将数据添加到缓冲区中,消费者也不会尝试从空缓冲区中删除数据。
解决方案: 如果缓冲区已满,生产者要么进入睡眠状态,要么丢弃数据。下一次消费者从缓冲区中移除一个项目时,它会通知生产者,后者会再次开始填充缓冲区。同样,如果消费者发现缓冲区为空,也可以进入睡眠状态。下次生产者将数据放入缓冲区时,它会唤醒睡眠中的消费者。 解决方案不充分可能导致僵局 这两个过程都在等待被唤醒。
在岗 在Java中使用线程的生产者-消费者解决方案, 我们已经使用 线程间通信 (等待()、通知()、睡眠()。在本文中,我们将使用 信号灯 实现同样的目标。
以下解决方案由四类组成:
- Q :尝试同步的队列
- 制片人: 正在生成队列条目的线程对象
- 消费者: 正在使用队列条目的线程对象
- 个人电脑: 创建单个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主页上,并帮助其他极客。
如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。