乐观的并发策略——基于CAS的自旋

悲观者与乐观者的作事方式彻底不同,悲观者的人生观是一件事情我必需要百分之百彻底控制才会去作,不然就认为这件事情必定会出问题;而乐观者的人生观则相反,凡事无论最终结果如何,他都会先尝试去作,大不了最后不成功。这就是悲观锁与乐观锁的区别,悲观锁会把整个对象加锁占为自有后才去作操做,乐观锁不获取锁直接作操做,而后经过必定检测手段决定是否更新数据。这一节将对乐观锁进行深刻探讨。算法

前面用Synchronized互斥锁属于悲观锁,它有一个明显的缺点,它无论数据存不存在竞争都加锁,随着并发量增长,且若是锁的时间比较长,其性能开销将会变得很大。有没有办法解决这个问题?答案是基于冲突检测的乐观锁。这种模式下,已经没有所谓的锁概念了,每条线程都直接先去执行操做,计算完成后检测是否与其余线程存在共享数据竞争,若是没有则让此操做成功,若是存在共享数据竞争则可能不断地从新执行操做和检测,直到成功为止,可叫CAS自旋。bash

乐观锁的核心算法是CAS(Compare and Swap),它涉及到三个操做数:内存值、预期值、新值。当且仅当预期值和内存值相等时才将内存值修改成新值。这样处理的逻辑是,首先检查某块内存的值是否跟以前我读取时的同样,如不同则表示期间此内存值已经被别的线程更改过,舍弃本次操做,不然说明期间没有其余线程对此内存值操做,能够把新值设置给此块内存。如图,有两个线程可能会差很少同时对某内存操做,线程二先读取某内存值做为预期值,执行到某处时线程二决定将新值设置到内存块中,若是线程一在此期间修改了内存块,则经过CAS便可以检测出来,假如检测没问题则线程二将新值赋予内存块。并发

你可能会发现一个疑问,比较和交换,从字面上就有两个操做了,更别说实际CAS可能会有更多的执行指令,他们是原子性的吗?若是非原子性又怎么保证CAS操做期间出现并发带来的问题?我是否是须要用上节提到的互斥锁来保证他的原子性操做?CAS确定是具备原子性的,否则就谈不上在并发中使用了,但这个原子性是由CPU硬件指令实现保证的,即便用JNI调用native方法调用由C++编写的硬件级别指令,jdk中提供了Unsafe类执行这些操做。另外,你可能想着CAS是经过互斥锁来实现原子性的,这样确实能实现,但用这种方式来保证原子性显示毫无心义。下面一个伪代码加深对CAS的理解:

public class AtomicInt {
    private volatile int value; 
    public final int get() {
        return value;
    }
	
	public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }
    
    public final boolean compareAndSet(int expect, int update) {
		Unsafe类提供的硬件级别的compareAndSwapInt方法;
    }
}
复制代码

其中最重要的方法是getAndIncrement方法,它里面实现了基于CAS的自旋。 如今已经了解乐观锁及CAS相关机制,乐观锁避免了悲观锁独占对象的现象,同时也提升了并发性能,但它也有缺点:机器学习

  1. 观锁只能保证一个共享变量的原子操做。如上例子,自旋过程当中只能保证value变量的原子性,这时若是多一个或几个变量,乐观锁将变得力不从心,但互斥锁能轻易解决,无论对象数量多少及对象颗粒度大小。
  2. 长时间自旋可能致使开销大。假如CAS长时间不成功而一直自旋,会给CPU带来很大的开销。
  3. ABA问题。CAS的核心思想是经过比对内存值与预期值是否同样而判断内存值是否被改过,但这个判断逻辑不严谨,假如内存值原来是A,后来被一条线程改成B,最后又被改为了A,则CAS认为此内存值并无发生改变,但其实是有被其余线程改过的,这种状况对依赖过程值的情景的运算结果影响很大。解决的思路是引入版本号,每次变量更新都把版本号加一。

乐观锁是对悲观锁的改进,虽然它也有缺点,但它确实已经成为提升并发性能的主要手段,并且jdk中的并发包也大量使用基于CAS的乐观锁。分布式

-------------推荐阅读------------高并发

个人2017文章汇总——机器学习篇性能

个人2017文章汇总——Java及中间件学习

个人2017文章汇总——深度学习篇ui

个人2017文章汇总——JDK源码篇spa

个人2017文章汇总——天然语言处理篇

个人2017文章汇总——Java并发篇


跟我交流,向我提问:

这里写图片描述

公众号的菜单已分为“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。

为何写《Tomcat内核设计剖析》

欢迎关注:

这里写图片描述
相关文章
相关标签/搜索