关于AtomicInteger的一些见解&底层分析

1. 前言

Java中对基本类型的变量的赋值和读取是原子操作,如 i = 1 这样的是原子操作,但 j = i,j++ 都不是原子操作,它们都进行了多次原子操作,可能会出现线程安全的问题,所以AtomicInteger就应运而生了,它是一个原子类,可以解决我们在多线程环境下 i++ 的线程安全问题。

2. CAS

AtomicInteger是对int类型的一个封装,提供原子性的访问和更新操作,其原子性操作的实现是基于CAS(compare-and-swap)技术,比较并交换
CAS,表现为一组指令,当利用CAS执行试图进行一些更新操作时。会首先比较当前数值,如果数值未变,代表没有其它线程进行并发修改,则成功更新。如果数值改变,则可能出现不同的选择,要么进行重试,要么就返回是否成功,也就是所谓的“乐观锁”。通俗点来说,CAS有三个待操作的数:当前值A、内存值Z、需要修改的新值B,假设当前值A与内存值Z相等,那就将内存值Z改为B,假设当前值A与内存值Z不相等,要么就重试,要么就放弃更新,将当前值与内存值进行对比,判断是否有被修改过,这就是CAS的核心。

在JAVA中,sun.misc.Unsafe 类提供了硬件级别的原子操作来实现这个CAS。 java.util.concurrent 包下的大量类都使用了这个 Unsafe.java 类的CAS操作。

关于CAS的更多介绍可以看这篇–>> 什么是CAS机制?

2.1 为什么要用CAS呢?

CAS相对于synchronized没有加锁,多个线程都可以直接操作共享资源,在实际去修改的时候才会去判断能否修改成功。

2.2 CAS的一些缺点

  1. CAS有个缺点就是会带来ABA问题,从CAS更新的时候,我们会发现它只会对比当前值和内存值是否相等。例如:

内存值V=100;
threadA 将100,改为50;
threadB 将100,改为50;
threadC 将50,改为100;

从线程A的角度说,这个值就是从未被修改过的,显然这是不合理的,因为这个V已经被B、C修改过了,这就是ABA问题,要解决ABA问题,Java也提供了AtomicStampedReference类供我们使用,其实就是加了一个版本,比较的就是内存值+版本是否一致.

在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。

  1. 循环时间长开销大:自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。

  2. 只能保证一个共享变量的原子操作:当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性。

3. AtomicInteger相关源码

public class AtomicInteger extends Number implements java.io.Serializable {
    //...//
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;
    //...//
}
  1. 从 AtomicInteger 的内部属性可以看出,它依赖于Unsafe类获得对象内存地址访问,偏移量valueOffset代表的该变量值在内存中的偏移地址,从而获取数据的
  2. 变量value用volatile修饰,保证了多线程之间的内存可见性,当前线程可以拿到value最新的值
  3. CAS操作保证了AtomicInteger 可以安全的修改value 的值

AtomicInteger 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。

CAS 的原理是拿期望的值和原本的一个值作比较,如果相同则更新成新的值。UnSafe 类的 objectFieldOffset() 方法是一个本地方法,这个方法是用来拿到“原来的值”的内存地址,返回值是 valueOffset。另外 value 是一个 volatile 变量,在内存中可见,因此 JVM 可以保证任何时刻任何线程总能拿到该变量的最新值。

3.1 应用场景

AtomicInteger提供原子操作来进行Integer的使用,通过线程安全的方式操作加减。
AtomicInteger是在使用非阻塞算法实现并发控制,适合一些高并发场景。

// 以原子的方式将给定发的值与当前的值进行相加,实际上就是等于线程安全版本的
// i = i + delta操作
public final int addAndGet(int delta) 
// 以原子的方式将给定发的值与当前的值进行相加,实际上就是等于线程安全版本的
// j = i; i += delta;
// return j
public final int getAndAdd(int delta) 
// 获取当前的值
public final int get()  

请添加图片描述

// 获取当前的值,并自增,相当于线程安全版本的i++操作
public final int getAndIncrement()
// 获取当前的值,并自减,相当于线程安全版本的i--操作
public final int getAndDecrement() 
// 获取当前的值,并自增,相当于线程安全版本的++i操作
public final int incrementAndGet() 
// 如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
boolean compareAndSet(int expect, int update) 
//...// 方法还有很多,就不一一列举了

以上的方法都可以在这个链接找到实例–>> Java AtomicInteger 相关方法的用法及代码示例

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SuZhan7710

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

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

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

打赏作者

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

抵扣说明:

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

余额充值