说道自旋锁就要从多线程下的锁机制说起,由于在多处理器系统环境中有些资源因为其有限性,有时需要互斥访问(互斥),这时会引入锁的机制,只有获取了锁的进程才能获取资源访问。即每次只能有且只有一个进程能获取锁,才能进入自己的临界区,同一时间不能两个或两个以上进程进入临界区,当退出临界区时释放锁。
设计互斥算法时总是会面临一种情况,即没有获得锁的进程怎么办?
<强>通常有2种处理方式:强>
一种是没有获得锁的调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,这就是本文的重点——自旋锁。他不用将线城阻塞起来(阻塞)。
另一种是没有获得锁的进程就阻塞(阻塞)自己,继续执行线程上的其他任务,这就是——互斥锁(包括内置锁同步还有ReentrantLock等等)。
CAS(比较和交换),即比较并交换,也是实现我们平时所说的自旋锁或乐观锁的核心操作。
它的实现很简单,就是用一个预期的值和内存值进行比较,如果两个值相等,就用预期的值替换内存值,并返回真的。否,则返回错误的。
任何技术的出现都是为了解决某些特定的问题,中科院要解决的问题就是保证原子操作。原子操作是什么,原子就是最小不可拆分的,原子操作就是最小不可拆分的操作,也就是说操作一旦开始,就不能被打断,知道操作完成。在多线程环境下,原子操作是保证线程安全的重要手段。举个例子来说,假设有两个线程在工作,都想对某个值做修改,就拿自增操作来说吧,要对一个整数我进行自增操作,需要基本的三个步骤:
1,读取我的当前值;
2,对我值进行加1操作,
3,将我值写回内存;
假设两个进程都读取了我的当前值,假设是0,这时候一线程对我加1了,B线程也加1,最后我的是1,而不是2。这就是因为自增操作不是原子操作,分成的这三个步骤可以被干扰。如下面这个例子,10个线程,每个线程都执行10000次我+ +操作,我们期望的值是100000,但是很遗憾,结果总是小于100000的。
静态int i=0; 公共静态孔隙add () { 我+ +; } 私有静态类+实现Runnable { @Override 公共空间run () { for (int k=0; k<10000; k + +) { add (); } } } 公共静态void main (String [] args)抛出InterruptedException { 线程线程[]=新线程[10]; (int i=0; i<10;我+ +){ [我]=新线程线程(新加()); 线程[我].start (); } (int i=0; i<10;我+ +){ 线程(我). join (); } System.out.println(我); }
既然这样,那怎么办。没错,也许你已经想到了,可以加锁或者利用同步实现,例如,将添加()方法修改为如下这样:
公共静态同步空白add () { 我+ +; }
或者,加锁操作,例如下面使用ReentrantLock(可重入锁)实现。
私有静态锁锁=新的ReentrantLock (); 公共静态孔隙add () { lock.lock (); 我+ +; lock.unlock (); }
既然用锁或同步关键字可以实现原子操作,那么为什么还要用CAS呢,因为加锁或使用同步关键字带来的性能损耗较大,而用CAS可以实现乐观锁,它实际上是直接利用了CPU层面的指令,所以性能很高。
上面也说了,中科院是实现自旋锁的基础,中科院利用CPU指令保证了操作的原子性,以达到锁的效果,至于自旋呢,看字面意思也很明白,自己旋转,翻译成人话就是循环,一般是用一个无限循环实现。这样一来,一个无限循环中,执行一个中科院操作,当操作成功,返回真实时,循环结束;当返回假时,接着执行循环,继续尝试CAS操作,直到返回真的。
其实JDK中有好多地方用到了中科院,尤其是java.util。信号,并发包下,比如CountDownLatch ReentrantLock中,再比如java . util . concurrent。原子包下,相信大家都用到过原子*,比如AtomicBoolean, AtomicInteger等。
这里拿AtomicBoolean来举个例子,因为它足够简单。
公共java类AtomicBoolean实现。可序列化的{ 私有静态最终长serialVersionUID=4654671469794556979 l;//设置使用Unsafe.compareAndSwapInt更新 私有静态最终不安全不安全=Unsafe.getUnsafe (); 私有静态最终长valueOffset; 静态{ 尝试{ valueOffset=unsafe.objectFieldOffset (AtomicBoolean.class.getDeclaredField(“价值”)); }捕捉(例外的前女友){抛出新的错误(ex);} } 私人动荡的int值; 公众最终布尔get () { 返回值!=0; } 公众最终布尔compareAndSet(布尔预计,布尔更新){ int e=期望& # 63;1:0; int u=更新& # 63;1:0; 返回unsafe.compareAndSwapInt (valueOffset, e, u); } }深入讲解我们说的CAS自旋锁到底是什么