先看一个有问题的只能轮替发生的生产者-消费者模型代码(源自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。
相关推荐
自旋锁不会引起调用者睡眠,如果一个执行线程试图获得一个已经被持有的自旋锁,那么线程就会一直进行忙循环,一直等待下去,在那里看是否该自旋锁的保持者已经释放了锁,\"自旋\"一词就是因此而得名。由于自旋锁使用...
我们描述了简化的辛结构,并表明“简化的主要汉密尔顿主义者”通过仅保留其主导项来再现自旋萨瑟兰模型。 运动方程的解是通过标准投影方法从紧凑的Lie群上的测地线上得出的,并且具有许多第一积分。 L.-C之前获得了...
CPU自旋锁优化2019年10月14日内容• 数据迁移问题 & 对性能影响• Spinlock Issues on Multi-Socket systemsCr
linux 自旋锁讨论记录,自旋锁与信号量有哪些差别,这里给出答案
信号量与自旋锁
我们讨论了Sklyanin的边界量子逆散射方法的广义版本,该方法适用于自旋1/2,三角sl(2)情况,对于该情况,扭曲周期和边界构造都作为极限情况。 然后,我们研究这种方法的准经典极限,从而得出了一组相互交换的守恒...
根据《多处理器编程的艺术》一书第七章“自旋锁与争用”编写的C++代码,演示了10种锁的实现。代码为本人学习研究所用,欢迎高手赐教。
本文章是关于信号量、互斥体和自旋锁的区别。
纯英文,专门解析自旋锁起因及解决方法,SQL SERVER DBA必备
基于SMP的Linux内核自旋锁分析.pdf
linux系统中基于自旋锁的进程调度的实现, 有代码和详细的文档说明,自旋锁(spinlock) 是用C和汇编指令实现的,有助于了解linux系统 内核的加锁机制。 很不错的哦。。。
redis实现分布式锁,自旋式加锁,lua原子性解锁
轻量级锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量 级锁使用产生的性能消耗。在解释轻量级锁的执行过程之前,先明白一点,轻量级锁所适应的场 景是线程交替执行同步块的情况...
在最近的文献中,我们指出了在Duffin-Kemmer-Petiau(DKP)形式主义的背景下,旋转粒子非最小矢量相互作用的解析解的一种误导性处理。 在这些论文中,作者在其主要结论中不适当地使用了非最小矢量相互作用。 我们...
在这两种情况下,后者的约束在约束BRST运算符Q C和自旋运算符σC i约束的约束面上超交换。 超代数{QC,Ôa,σC i}的封闭性保证了最终的规范不变拉格朗日公式与壳外代数约束Ôa相容,constraints Hilbert空间的场和...
对于互斥锁,如果资源已经被占用,自愿申请者只能进入睡眠状态。但自旋锁不会引起调用者睡眠
线程同步(Thread Synchronization)是并行编程中非常重要的通讯手段,其中最典型的应用就是用Pthreads提供的锁机制(lock)来对多个线程之间共 享的临界区(Critical Section)进行保护(另一种常用的同步机制是barrier)。
在此注释中,我们研究了它们的半经典SU(2)频谱流动扇区的出现,作为基本量子自旋链的Landau-Lifshitz极限。 我们在相干状态图中考虑传播器,发现时间间隔与晶格间距成比例地离散。 在时间和空间都连续的Landau-...
度量波动对自旋2模式之一进行编码,但是它们不会在经典矩阵模型中传播。 认为重力是在考虑了引力项的情况下产生的。 这种形式主义可以应用于[1]的宇宙FLRW时空解,它是Hn4的预测。 我们在这些空间的切向波动之间...
一种Linux内核自旋锁死锁检测机制的设计与实现.pdf