AtomicInteger原理与用法详解(看这篇就够了)

AtomicInteger原理与用法详解(看这篇就够了)-mikechen

java并发编程直是学习和Java面试的重点,这篇主要是讲解AtomicInteger @mikechen

AtomicInteger简介

AtomicInteger 是一个支持原子操作的 Integer 类,它提供了原子自增方法、原子自减方法以及原子赋值方法等。

 

为什么需要AtomicInteger

谈到线程安全,会首先想到了synchronized 和 Lock,但是这种方式又有一个名字,叫做互斥锁,一次只能有一个持有锁的线程进入,再加上还有不同线程争夺这个机制,效率比较低,所以又称 悲观锁

与之相对应,就有了 乐观锁 的概念,它不加锁去完成某项操作,如果因为冲突失败就重试,直到成功为止。

AtomicInteger 保证线程安全就是使用了乐观锁,所以相对于悲观锁,效率更高。

 

AtomaticInteger的应用场景

AtomaticInteger最典型的应用场景是计数,比如我们要统计并发插入10万条数据的耗时,我们需要对插入的数据计数。

普通的int变量在多线程环境下的++操作,是线程不安全的,前一个操作可能会被后一个操作所覆盖,所以统计的技术往往小于准确值,这时候就可以使用AtomaticInteger,使用非常简单:

private AtomicInteger counter = new AtomicInteger(0);//初始计数为0
// doSomething,执行操作之后,计数即可
int count = counter.incrementAndGet();

 

AtomicInteger常见用法

要将它用作计数器,AtomicIntegerclass提供了一些以原子方式执行加法和减法操作的方法。

addAndGet()- 以原子方式将给定值添加到当前值,并在添加后返回新值。
getAndAdd() - 以原子方式将给定值添加到当前值并返回旧值。
incrementAndGet()- 以原子方式将当前值递增1并在递增后返回新值。它相当于i ++操作。
getAndIncrement() - 以原子方式递增当前值并返回旧值。它相当于++ i操作。
decrementAndGet()- 原子地将当前值减1并在减量后返回新值。它等同于i-操作。
getAndDecrement() - 以原子方式递减当前值并返回旧值。它相当于-i操作。

下面具体的使用方式:

public class Main{
    public static void main(String[] args)
    {
        AtomicInteger atomicInteger = new AtomicInteger(100);
         
        System.out.println(atomicInteger.addAndGet(2));         //102
        System.out.println(atomicInteger);                      //102
         
        System.out.println(atomicInteger.getAndAdd(2));         //102
        System.out.println(atomicInteger);                      //104
         
        System.out.println(atomicInteger.incrementAndGet());    //105  
        System.out.println(atomicInteger);                      //105  
                 
        System.out.println(atomicInteger.getAndIncrement());    //105
        System.out.println(atomicInteger);                      //106
         
        System.out.println(atomicInteger.decrementAndGet());    //105
        System.out.println(atomicInteger);                      //105
         
        System.out.println(atomicInteger.getAndDecrement());    //105
        System.out.println(atomicInteger);                      //104
    }
}

 

AtomicInteger的实现原理

AtomicInteger 是一个支持原子操作的 Integer 类,就是保证对AtomicInteger类型变量的增加和减少操作是原子性的,不会出现多个线程下的数据不一致问题。

接下来通过源代码来看AtomicInteger具体是如何实现的原子操作,首先看AtomicInteger 的自增方法,incrementAndGet() 方法,代码如下:

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

通过源码,可以知道,这个方法的做法为先获取到当前的 value 属性值,然后将 value 加 1,赋值给一个局部的 next 变量,然而,这两步都是非线程安全的,但是内部有一个死循环,不断去做compareAndSet操作,直到成功为止,也就是修改的根本在compareAndSet方法里面。

CAS(Compare And Swap)即比较并交换,CAS 是乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。

它包含三个参数:V 内存值,预期值 A,要修改的新值 B,当且仅当预期值 A 和内存值 V 相同时,将内存值 V 修改为 B,否则什么都不做,原理图如下所示:

AtomicInteger原理与用法详解(看这篇就够了)-mikechen

这样使用CAS的方法就保证了原子操作,除此之外还有一个volatile修饰的value值也是关键。

这里我们结合AtomicInteger类的来说,源码如下:

public class AtomicInteger extends Number implements java.io.Serializable {
// 省略部分代码
// 使用volatile修饰了一个变量用来存储数值
    private volatile int value;
// 构造函数直接初始化value值
public AtomicInteger(int initialValue) {
value = initialValue; }
// 无参构造,此时value默认为0 public AtomicInteger() {
 }

其中 value 是一个 volatile 变量,在内存中可见,因此 JVM 可以保证任何时刻任何线程总能拿到该变量的最新值。

private volatile int value;

volatile 是一种稍弱的同步机制,用来确保将变量的更新操作通知到其他线程。

当把变量声明为 volatile 类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。

在访问 volatile 变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此 volatile 变量是一种比 sychronized 关键字更轻量级的同步机制。

一句话总结ActomicInteger的实现原理:主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。

 

AtomicInteger总结

使用 AtomicInteger替换普通 int 类型执行自增的原子操作,能够保证了线程安全,AtomicInteger由硬件提供原子操作指令实现的,在非激烈竞争的情况下,开销更小,速度更快。。

作者简介

陈睿|mikechen,10年+大厂架构经验,BAT资深面试官,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

👇阅读更多mikechen架构文章👇

阿里架构 |双11秒杀 |分布式架构 |负载均衡 |单点登录 |微服务 |云原生 |高并发 |架构师

以上

关注作者「mikechen」公众号,获取更多技术干货!

后台回复架构,即可获取《阿里架构师进阶专题全部合集》,后台回复面试即可获取《史上最全阿里Java面试题总结

评论交流
    说说你的看法