Java多线程进阶(12)—— J.U.C之atomic框架:AtomicInteger

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

一、AtomicInteger简介

AtomicInteger,应该是atomic框架中用得最多的原子类了。顾名思义,AtomicInteger是Integer类型的线程安全原子类,可以在应用程序中以原子的方式更新int值。

1. 创建AtomicInteger对象

先来看下AtomicInteger对象的创建。

AtomicInteger提供了两个构造器,使用默认构造器时,内部int类型的value值为0:
AtomicInteger atomicInt = new AtomicInteger();

AtomicInteger类的内部并不复杂,所有的操作都针对内部的int值——value,并通过Unsafe类来实现线程安全的CAS操作:

2. AtomicInteger的使用

来看下面这个示例程序:

    public class Main {
        public static void main(String[] args) throws InterruptedException {
            AtomicInteger ai = new AtomicInteger();
    
            List<Thread> list = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                Thread t = new Thread(new Accumlator(ai), "thread-" + i);
                list.add(t);
                t.start();
            }
    
            for (Thread t : list) {
                t.join();
            }
    
            System.out.println(ai.get());
        }
    
        static class Accumlator implements Runnable {
            private AtomicInteger ai;
    
            Accumlator(AtomicInteger ai) {
                this.ai = ai;
            }
    
            @Override
            public void run() {
                for (int i = 0, len = 1000; i < len; i++) {
                    ai.incrementAndGet();
                }
            }
        }
    }

上述代码使用了AtomicInteger的 incrementAndGet 方法,以原子的操作对int值进行自增,该段程序执行的最终结果为10000(10个线程,每个线程对AtomicInteger增加1000),如果不使用AtomicInteger,使用原始的int或Integer,最终结果值可能会小于10000(并发时读到了过时的数据或存在值覆盖的问题)。

我们来看下 incrementAndGet 内部:

内部调用了Unsafe类的 getAndAddInt 方法,以原子方式将value值增加1,然后返回增加前的原始值。

注意,上述是JDK1.8的实现,在JDK1.8之前,上述方法采用了自旋+CAS操作的方式:

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

3. AtomicInteger的特殊方法说明

AtomicInteger中有一个比较特殊的方法—— lazySet :

lazySet 方法是 set 方法的不可见版本。什么意思呢?

我们知道通过volatile修饰的变量,可以保证在多处理器环境下的“可见性”。也就是说当一个线程修改一个共享变量时,其它线程能立即读到这个修改的值。volatile的实现最终是加了内存屏障:

  1. 保证写volatile变量会强制把CPU写缓存区的数据刷新到内存
  2. 读volatile变量时,使缓存失效,强制从内存中读取最新的值
  3. 由于内存屏障的存在,volatile变量还能阻止重排序

lazySet 内部调用了Unsafe类的 putOrderedInt 方法,通过该方法对共享变量值的改变,不一定能被其他线程立即看到。也就是说以普通变量的操作方式来写变量。

为什么会有这种奇怪方法?什么情况下需要使用lazySet呢?

考虑下面这样一个场景:

    private AtomicInteger ai = new AtomicInteger();
    lock.lock();
    try
    {
        // ai.set(1);
    }
    finally
    {
        lock.unlock();
    }

由于锁的存在:

  • lock() 方法获取锁时,和volatile变量的读操作一样,会强制使CPU缓存失效,强制从内存读取变量。
  • unlock() 方法释放锁时,和volatile变量的写操作一样,会强制刷新CPU写缓冲区,把缓存数据写到主内存

所以,上述ai.set(1)可以用ai.lazySet(1)方法替换:

由锁来保证共享变量的可见性,以设置普通变量的方式来修改共享变量,减少不必要的内存屏障,从而提高程序执行的效率。

二、类/接口说明

类声明

构造器

接口声明

方法声明描述
intaccumulateAndGet(intx,IntBinaryOperatoraccumulatorFunction)使用IntBinaryOperator对当前值和x进行计算,并更新当前值,返回计算后的新值
intaddAndGet(intdelta)以原子方式将给定值与当前值相加,返回相加后的新值
booleancompareAndSet(intexpect,intupdate)如果当前值==expect,则以原子方式将该值设置为给定的更新值(update)
intdecrementAndGet()以原子方式将当前值减1,返回新值
intget()获取当前值
intgetAndAccumulate(intx,IntBinaryOperatoraccumulatorFunction)使用IntBinaryOperator对当前值和x进行计算,并更新当前值,返回计算前的旧值
intgetAndAdd(intdelta)以原子方式将给定值与当前值相加,返回旧值
intgetAndDecrement()以原子方式将当前值减1,返回旧值
intgetAndIncrement()以原子方式将当前值加1,返回旧值
intgetAndSet(intnewValue)以原子方式设置为给定值,并返回旧值
intgetAndUpdate(IntUnaryOperatorupdateFunction)使用IntBinaryOperator对当前值进行计算,并更新当前值,返回计算前的旧值
intincrementAndGet()以原子方式将当前值加1,返回新值
voidlazySet(intnewValue)设置为给定值,但不保证值的改变被其他线程立即看到
voidset(intnewValue)设置为给定值
intupdateAndGet(IntUnaryOperatorupdateFunction)使用IntBinaryOperator对当前值进行计算,并更新当前值,返回计算后的新值
booleanweakCompareAndSet(intexpect,intupdate)weakCompareAndSet无法保证除操作目标外的其他变量的执行顺序(编译器和处理器为了优化程序性能而对指令序列进行重新排序),同时也无法保证这些变量的可见性。

三、其它原子类

与 AtomicInteger 类似的原子类还有 AtomicBoolean 和 AtomicLong ,底层都是通过Unsafe类做CAS操作,来原子的更新状态值。可以参考Oracle官方文档:https://docs.oracle.com/javase/8/docs/api/ ,不再赘述。

  • 10
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值