zoukankan      html  css  js  c++  java
  • 乐观锁与悲观锁及其实现

    乐观锁  每次操作时不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止
    悲观锁  是会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
    乐观锁可以使用volatile+CAS原语实现,读取内存值的方式实现了乐观锁,方法:第一,比较内存值和期望值;第二,替换内存值为要替换值。
    悲观锁可以使用synchronize的以及Lock。

    CAS是单词compare and set的缩写,意思是指在set之前先比较该值有没有变化,只有在没变的情况下才对其赋值。

    例如AtomicInteger的incrementAndGet的实现就用到了compareAndSet(CAS),如下代码所示

     public final int incrementAndGet() {
            for (;;) {
                int current = get();
                int next = current + 1;
                if (compareAndSet(current, next))
                    return next;
            }
        }

    首先可以看到他是通过一个无限循环(spin)直到increment成功为止.  
    循环的内容是
    1.取得当前值
    2.计算+1后的值
    3.如果当前值还有效(没有被)的话设置那个+1后的值
    4.如果设置没成功(当前值已经无效了即被别的线程改过了), 再从1开始.

    2. compareAndSet的实现

    public final boolean compareAndSet(int expect, int update) {
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        }

    直接调用的是UnSafe这个类的compareAndSwapInt方法
    全称是
    sun.misc.Unsafe. 这个类是Oracle(Sun)提供的实现. 可以在别的公司的JDK里就不是这个类了

    3. compareAndSwapInt的实现

        /**
         * Atomically update Java variable to <tt>x</tt> if it is currently
         * holding <tt>expected</tt>.
         * @return <tt>true</tt> if successful
         */
        public final native boolean compareAndSwapInt(Object o, long offset,
                                                      int expected,
                                                      int x);

    此外,java.util.concurrent.ConcurrentLinkedQueue类全是采用的非阻塞算法,里面没有使用任何锁,全是基于CAS操作实现的。CAS操作可以说是JAVA并发框架的基础,整个框架的设计都是基于CAS操作的。

    优点:

    非阻塞算法(通常叫作乐观算法)相对于基于锁的版本有几个性能优势。首先,它用硬件的原生形态代替 JVM 的锁定代码路径,从而在更细的粒度层次上(独立的内存位置)进行同步,失败的线程也可以立即重试,而不会被挂起后重新调度。更细的粒度降低了争用的机会,不用重新调度就能重试的能力也降低了争用的成本。即使有少量失败的 CAS 操作,这种方法仍然会比由于锁争用造成的重新调度快得多。

    缺点:

    1、ABA问题

    CAS操作容易导致ABA问题,也就是在做a++之间,a可能被多个线程修改过了,只不过回到了最初的值,这时CAS会认为a的值没有变。a在外面逛了一圈回来,你能保证它没有做任何坏事,不能!!也许它讨闲,把b的值减了一下,把c的值加了一下等等,更有甚者如果a是一个对象,这个对象有可能是新创建出来的,a是一个引用呢情况又如何,所以这里面还是存在着很多问题的,解决ABA问题的方法有很多,可以考虑增加一个修改计数,只有修改计数不变的且a值不变的情况下才做a++,也可以考虑引入版本号,当版本号相同时才做a++操作等,这和事务原子性处理有点类似!

    2、比较花费CPU资源,即使没有任何争用也会做一些无用功。

    3、会增加程序测试的复杂度,稍不注意就会出现问题。

    总结:

    可以用CAS在无锁的情况下实现原子操作,但要明确应用场合,非常简单的操作且又不想引入锁可以考虑使用CAS操作,当想要非阻塞地完成某一操作也可以考虑CAS。不推荐在复杂操作中引入CAS,会使程序可读性变差,且难以测试,同时会出现ABA问题。

    参考 

    http://www.ibm.com/developerworks/cn/java/j-jtp04186/

    http://singleant.iteye.com/blog/1405478     实现简单乐观独占锁

  • 相关阅读:
    ios开发之多线程---GCD
    三:新浪微博--主框架的搭建
    二:新浪微博:第三方框架管理工具CocoaPods的安装和使用
    PHP Fatal Error: call to undefined function mysql_connect() [duplicate]
    ZooKeeper 3.4.5 分布式环境搭建详解
    coreLocation说明,作者写的很用心,收藏
    动画设置参数,有数值,收藏了
    Java 设计模式——组合模式
    NSURL基本操作示例说明
    关于应用程序启动,你可能不知道的东西
  • 原文地址:https://www.cnblogs.com/pingh/p/3505486.html
Copyright © 2011-2022 走看看