一步一步认识java并发库(一)cas指令
大学毕业,工作多年,终日惶恐,碌碌无为,不安于,重复枯燥的工作。
内心的郁闷,长时间的加班,又加上日渐虚落的身体。
毅然辞去手头工作,修身,反省,回顾,总结,重新规划。
这个系列的博文,是对java并发库学习的一个归纳和总结。
希望通过一步一步模仿java并发库的实现。能够对并发有一个比较深入和全面的了解。
首先我们介绍一下cas(Compare and Swap)指令,这个指令是java并发库的基石。
在具体讲述之前,我先提两个问题:
1. 为什么需要cas指令
2. 什么是cas指令
3. 如何用cas指令实现一个简单的锁
好,我们正式开始,我们先通过一个例子来回答问题1和2
有一个变量state(初始值为0),有两个线程同时改变state的值
有线程thread1执行逻辑,判断state是否是0,是,将state设为1;
有线程thread2执行逻辑,判断state是否是0,是,将state设为2。(如图1)
因为是多线程,各个线程间的指令执行顺序不可控,如果是下面的执行顺序,就会带来问题
指令1(if state==0)->指令3(if state==0)
->指令2(state=1)->指令4(state=2)
->thread1返回true->thread2返回true
最终state==2,两个线程都返回true,
但对于thread1这矛盾了(按thread1的语义,返回true后,state应该为等于1)
有这个问题怎么办呢?
这时候cas指令该上场了
(这里有过编程经验的同学,会有疑问,为什么不直接加锁,
而用cas指令,这个问题在下面介绍cas原理的时候,会涉及到),
cas(i,a,b)的语义是if (i==a) {i=b;return ture} else return false;
并带有原子性(相当于前面的代码语句是一条指令),
既,有多个线程执行相同条件的cas(i,a,b)只有一个线程会成功
这里注意一下,上面说的cas语义代码,并不是真的代码,只是指令功能上的一个解释
(cas指令是cpu级的指令而不是一段代码)。
这里简单介绍一下cas指令原理。
cpu实现原子性变量修改有两种方式:
1.总线锁(普通的锁可能对应的方式)
总线锁,指多个处理器,如果处理器1 要修改变量a
(原子性的,其他处理器不能同时修改),处理器1就会用总线锁,
因为总线是共享的,总线被锁住,会照成其他处理器的浪费
2.缓存锁(cas对应的方式)
缓存锁简单来说,就是每个处理器都有对应的缓存(处理器是直
接操作缓存的,通过缓存再操作内存),
如果处理器1 要修改变量a(原子性的,其他处理器不能同时修
改),处理器1就会发出缓存锁的信号,其他处理器,就会明白不能修改变量a。
这种情况下,总线还可以用,其他cpu可以继续处理。
所以缓存锁(cas)相对总线锁(普通加锁),更能利用资源。
要详细了解cas原理的同学可以参考,
[缓存一致性](http://www.infoq.com/cn/articles/cache-coherency-primer/)
(http://zl198751.iteye.com/blog/1848575)
好,只要将前面的线程代码改成
thread1: return cas(state,0,1);
thread2: return cas(state,0,2);(如图2)
就能解决上面的语义矛盾问题了(因为多个相同条件的cas执行,只有一个会成功)
接下来我们用cas指令来解决问题3
state=0;//0代表未锁住,1代表锁住
public boolen lock(){
return cas(state,0,1);
}
//只有获得锁后才能调用
public boolean unlock(){
return cas(state,1,0);
}
当然实际的锁,还要解决多次获取,中断响应等,后面我们会具体讲到
同学们也可以看一下并发包的实际源码,体会一下cas的用法