AtomicInteger有关CAS

9 篇文章 0 订阅

AtomicInteger

public static void main(String[] args) {
        AtomicInteger ai = new AtomicInteger(20);
        System.out.println(ai.incrementAndGet());
    }

在学习AtominInteger时,第一次接触了CAS,也就是在自增的时候使用CAS自旋锁解决并发问题。调用的方法时incrementAndGet方法。而这个自增的操作本质上不是一个原子性操作,但是改方法在没有加synchronized的情况下实现了线程安全。下面深入学习CAS:

    /**
     * Atomically increments by one the current value.
     *  以原子性的方法,将当前值+1
     * @return the updated value
     */
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

这里用到了unsafe变量,该变量比较重要,以后解答,先看getAndAddInt方法

    //操作源码,传入三个值,V,A,B,第一个V是AtomicInteger维护的变量value,表示当前数的实际值,他被volatile修饰来保证其可见性。第二个值A为当前希望操作的值,B为当前希望修改后的值。
    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。首先CAS,compareAndSet方法将A与实际值V进行比对,如果相等,那么就可以操作修改为B,如果不等那就放弃操作。

这样保证了修改的一致性,但是不能保证修改的原子性,因为他有两个操作,一个是比对,第二个是修改。假设现在线程1需要将value = 1修改为2,那么比对A=1和V=1,发现相同那么修改。此时线程2比对A=1和V=1,发现也相同,把V修改为3。线程2执行完毕,而在线程1中,此时的V已经是3了,但是线程1已经经历过了比对操作,将3又修改为了2,这样就造成了严重的原子性问题。

这里是基于HotSpot底层调用操作系统来完成原子性操作的,在操作系统执行比对并修改的CAS操作时,加上了底层锁,保证了操作原子性,而我们使用Java的时候全然不知。这是操作系统内存的缓存行锁,也有可能是总线锁(这是属于硬件级别的锁,但是在我们的代码中看是没有锁的)。

最终实现(CPU汇编指令):

在最底层采用了lock cmpxchg 指令实现了cas(cmpxchg不是原子性的,所以加入lock指令)

硬件:

lock指令在执行后面指令的时候锁定一个北桥信号(不采用锁总线的方式)

用户态和内核态:

早期一些设备与计算机连接就可以直接使用底层方法操作硬件,这就意味着如果操作不当可能会让硬件死机等一些操作。为此有了用户态和内核态,将计算机最底层操作硬件的方法都集成到内核态进行统一处理,内核态一般都是操作系统所在的范围。而我们使用的jvm对于操作系统来说只是一个小程序,jvm属于用户态。当我们需要加锁时,如果一上来就给内核加锁来回切换用户态内核态,那就会造成极大的内存消耗,而cas操作就没有。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aristocrat l

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值