先看一个有问题的只能轮替发生的生产者-消费者模型代码(源自http://www.iteye.com/problems/96126的问题):
//生产/消费者模式 public class Basket { Lock lock = new ReentrantLock(); // 产生Condition对象 Condition produced = lock.newCondition(); Condition consumed = lock.newCondition(); boolean available = false; public void produce() throws InterruptedException { lock.lock(); try { if (available) { produced.await(); // 放弃lock进入睡眠 } // 生产 System.out.println("put"); available = true; consumed.signal(); // 发信号唤醒等待这个Condition的线程 } finally { lock.unlock(); } } public void consume() throws InterruptedException { lock.lock(); try { if (!available) { consumed.await(); // 放弃lock进入睡眠 } /* 消费 */ System.out.println("get"); available = false; produced.signal(); // 发信号唤醒等待这个Condition的线程 } finally { lock.unlock(); } } }
测试程序:
public static void main(String[] args) throws InterruptedException { final Basket basket = new Basket(); // 定义一个producer Runnable producer = new Runnable() { public void run() { try { basket.produce(); } catch (InterruptedException ex) { ex.printStackTrace(); } } }; // 定义一个consumer Runnable consumer = new Runnable() { public void run() { try { basket.consume(); } catch (InterruptedException ex) { ex.printStackTrace(); } } }; // 各产生10个consumer和producer ExecutorService service = Executors.newCachedThreadPool(); for (int i = 0; i < 4; i++) service.submit(producer); // Thread.sleep(2000); for (int i = 0; i < 4; i++) service.submit(consumer); service.shutdown(); }
本来想要的效果是一次put之后一次get,但是实际情况可能会出现两次put,或者先出现两次get,即在没有put之前就已经有get了,更甚至还可能出现程序卡死,即出现了4次put,3次get后停住了,或者3次put、4次get后停住了的现象。
原因分析:
当经过put、get之后,假如此时available为true,对于produce()方法可能出现下面情况:
一个线程在等待lock;
一个线程处于await
此时其他线程在调用consume()方法后,会把available设为false,并发送给生产线程发送信号,来唤醒处于await()的线程,之后会调用unlock()方法,让处于等待lock()的线程去竞争这个锁。此时会出现两种情况:
1. 处于等待lock锁的线程竞争到锁
2. 处于await的线程被唤醒,获取锁
如果是第2种情况,则一切正常。但是如果是等待lock()锁的线程竞争到锁,会出现下面情况:
由于处于await的线程后获取锁,但是此时available已经为true了,由于使用if,而不是while自旋锁,因此就会开始说的哪几种情况。
何为Java中的自旋锁:为了防止假唤醒,保存信号的成员变量放在一个while循环里接受检查,而不是在if表达式里。这样的一个while循环叫做自旋锁(校注:这种做法要慎重,目前的JVM实现自旋会消耗CPU,如果长时间不调用doNotify方法,doWait方法会一直自旋,CPU会消耗太大)。被唤醒的线程会自旋直到自旋锁(while循环)里的条件变为false。
相关推荐
在计算机科学中,"生产者消费者问题"是一个经典的多线程同步问题,它涉及到了并发控制和资源管理。这个问题的基本设定是有一群生产者线程负责生成数据,而一群消费者线程负责处理这些数据。生产者和消费者共享一个...
在生产者-消费者模型中,可能需要使用互斥锁防止同时访问缓冲区,以及使用条件变量(condition variables)来同步线程的执行,例如当缓冲区满时通知生产者停止生产,或当缓冲区空时唤醒消费者进行消费。 8. **线程...
- **示例代码**:使用`ReentrantLock`实现简单的生产者-消费者模型,展示如何在生产者和消费者之间同步数据交换。 - **性能对比**:比较`ReentrantLock`与`synchronized`关键字在特定场景下的性能差异。 ### 结论 ...
生产者线程会在缓冲区满时调用`wait()`,消费者线程则在缓冲区为空时调用`wait()`。在它们被唤醒时,都需要检查缓冲区的状态,以确认是否可以进行生产或消费。如果检查结果不满足条件,线程就需要再次等待。 ```cpp...
7. 唤醒策略:Disruptor使用一种高效的唤醒机制,当消费者等待新事件时,生产者会唤醒它们,而不是让它们持续轮询,这样可以降低CPU使用率。 在使用Disruptor C++版时,你可以参考提供的示例代码来理解和实现自己的...
- **LinkedBlockingQueue/ArrayBlockingQueue**:线程安全的队列,适用于生产者消费者模型。 7. **并发编程最佳实践** - **避免过度同步**:只同步必要的代码区域。 - **尽量使用并发工具类**:而非手动管理锁。...
这种队列非常适合用来实现生产者-消费者模型,在该模型中,生产者线程负责向队列中添加元素,而消费者线程负责从队列中取出元素进行处理。 ### Callable与Future - **Callable接口**类似于`Runnable`接口,但它...
- **生产者消费者模型**(Chap6, Chap7):这是一个经典的多线程问题,涉及到如何在不引入数据竞争的情况下平衡生产者和消费者的速率。 - **基于select的I/O模型和多线程文件下载**(Chap8):select函数用于监听多...
生产者-消费者模型是一种经典的多线程设计模式,其中生产者线程生成数据,消费者线程消耗数据,通常通过队列作为缓冲区来协调两者。 总的来说,C++11在并发和多线程编程方面的改进极大地增强了语言的能力,使得...
通过生产者消费者模型理解等待唤醒机制.mp4 Condition的使用及原理解析.mp4 使用Condition重写waitnotify案例并实现一个有界队列.mp4 深入解析Condition源码.mp4 实战:简易数据连接池.mp4 线程之间通信之join应用与...
并发队列通常用于线程间通信,如生产者-消费者模型。可以使用阻塞队列(Blocking Queue),它通过条件变量(Condition)来实现等待和唤醒机制。当队列满时,生产者线程会被阻塞,直到有空间可用;反之,当队列空时...
2. 通信方式:wait()、notify()和notifyAll()方法,生产者消费者模型(BlockingQueue)、管程(Monitor)。 四、Java多线程源码分析 深入理解Java线程库的源码有助于优化多线程程序。例如,synchronized的实现基于 ...
第30节通过生产者消费者模型理解等待唤醒机制00:20:50分钟 | 第31节Condition的使用及原理解析00:17:40分钟 | 第32节使用Condition重写wait/notify案例并实现一个有界队列00:22:05分钟 | 第33节深入解析Condition...
第30节通过生产者消费者模型理解等待唤醒机制00:20:50分钟 | 第31节Condition的使用及原理解析00:17:40分钟 | 第32节使用Condition重写wait/notify案例并实现一个有界队列00:22:05分钟 | 第33节深入解析Condition...
第30节通过生产者消费者模型理解等待唤醒机制00:20:50分钟 | 第31节Condition的使用及原理解析00:17:40分钟 | 第32节使用Condition重写wait/notify案例并实现一个有界队列00:22:05分钟 | 第33节深入解析Condition...
第30节通过生产者消费者模型理解等待唤醒机制00:20:50分钟 | 第31节Condition的使用及原理解析00:17:40分钟 | 第32节使用Condition重写wait/notify案例并实现一个有界队列00:22:05分钟 | 第33节深入解析Condition...
在多线程环境下,多个生产者线程可以同时向队列添加元素,而多个消费者线程也可以同时从中取出元素,这种设计模式提高了系统的并行处理能力。 在Java中,JDK的Concurrent包并没有提供原生的MPMC队列实现。然而,有...