zoukankan      html  css  js  c++  java
  • Java的CAS刨析

    一、什么是CAS

    CAS的全称叫做Compare And Swap,即比较并交换,是一种原子操作,同时CAS是一种乐观机制。在java.util.concurrent包中的很多功能都是建立在CAS之上。如 ReenterLock 内部的 AQS,各种原子类,其底层都用 CAS来实现原子操作。

    二、如何解决并发安全问题

    在Java并发中,我们最初接触的应该就是synchronized关键字了,但是synchronized属于重量级锁,很多时候会引起性能问题,volatile也是个不错的选择,但是volatile不能保证原子性,只能在某些场合下使用。

    像synchronized这种独占锁属于悲观锁,它是在假设一定会发生冲突的,那么加锁恰好有用,除此之外,还有乐观锁,乐观锁的含义就是假设没有发生冲突,那么我正好可以进行某项操作,如果要是发生冲突呢,那我就重试直到成功,乐观锁最常见的就是CAS。

    最常见的就是i++的例子,如下面代码所示:

    public class Test {
    
        public volatile int i;
    
        public synchronized void add() {
            i++;
        }
    }

    这种方法在性能上可能会差一点,我们还可以使用AtomicInteger,就可以保证i原子的++了。

    public class Test {
    
        public AtomicInteger i;
    
        public void add() {
            i.getAndIncrement();
        }
    }

    我们来看一下getAndIncrement()方法的内部是什么。

    /**
         * Atomically increments by one the current value.
         *
         * @return the previous value
         */
        public final int getAndIncrement() {
            return unsafe.getAndAddInt(this, valueOffset, 1);
        }

    再深入到getAndAddInt():

    public final int getAndAddInt(Object var1, long var2, int var4) {
            int var5;
            do {
                var5 = this.getIntVolatile(var1, var2);
            } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    
            return var5;
        }

    可以看到,CAS 的思想很简单:三个参数,一个当前内存值 V、旧的预期值 A、即将更新的值 B,当且仅当预期值 A 和内存值 V 相同时,将内存值修改为 B 并返回 true,否则什么都不做,并返回 false。代码中首先调用getIntVolatile取到内存中的值,再用当前值与内存中的值进行比较,如果相等则写入新的值,并返回true循环结束,反之继续取值知道当前值与预期值相等。其中compareAndSwapInt是同通过cmpxchgl CPU指令来完成的是个原子操作。

    三、CAS常见问题

    1、ABA问题

    如线程 1 从内存位置 V 取出 A,这时候线程 2 也从内存位置 V 取出 A,此时线程 1 处于挂起状态,线程 2 将位置 V 的值改成 B,最后再改成 A,这时候线程 1 再执行,发现位置 V 的值没有变化,尽管线程 1 也更改成功了,但是不代表这个过程就是没有问题的。

    2、自旋问题

    从源码可以知道所说的自选无非就是操作结果失败后继续循环操作,这种操作也称之为自旋锁,是一种乐观锁机制,一般来说都会给一个限定的自选次数,防止进入死循环。自旋锁的优点是不需要休眠当前线程,因为自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是休眠当前线程是提高并发性能的关键点,这是因为减少了很多不必要的线程上下文切换开销。但是,如果 CAS 一直操作不成功,会造成长时间原地自旋,会给 CPU 带来非常大的执行开销。

  • 相关阅读:
    为什么要写技术博客?
    MySQL开发总结
    如何在Linux实现自动运行程序
    SSH无密码登录
    PHP版本MS17-010检测小脚本
    Msf的一些常用操作
    bypass safedog upload
    mysql拿webshell总结
    web端MSF搭建
    【漏洞复现】Tomcat CVE-2017-12615 远程代码执行漏洞
  • 原文地址:https://www.cnblogs.com/ChenBingJie123/p/13917325.html
Copyright © 2011-2022 走看看