行业资讯 2025年08月6日
0 收藏 0 点赞 884 浏览 6783 个字
摘要 :

文章目录 1. 什么是CAS 2.CAS的原理 3.CAS的ABA问题 4. Atomic原子类 5.Unsafe类 1、Unsafe 提供的 CAS 方法 2、获取属性偏移量 3、根据属性的偏移量获取属性的最新值:……




  • 1. 什么是CAS
  • 2.CAS的原理
  • 3.CAS的ABA问题
  • 4. Atomic原子
  • 5.Unsafe类
    • 1、Unsafe 提供的 CAS 方法
    • 2、获取属性偏移量
    • 3、根据属性的偏移量获取属性的最新值:

    本文接前文Java多线程线程同步详解(1)继续讲解线程同步问题,主要涉及CAS算法、原子类和UnSafe类。

    1. 什么是CAS

    CAS,为Compare And Swap的缩写,中文翻译成比较并交换,是由硬件实现的。

    CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。

    2.CAS的原理

    在把值写到内存中时,会再次读取该地址的值,如果发现主存中的值与一开始读取到的值不同,则放弃写入(即撤销本次操作);否则就更新进去。

    使用CAS实现一个线程安全的计数器:

    public class CASTest {
        public static void main(String[] args) {
            CASCounter casCounter = new CASCounter();
            for(int i = 0; i < 1000; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName() + \": \" + casCounter.increment());
                    }
                }, \"Thread\" + i).start();
            }
        }
    
    }
    
    class CASCounter{
    
        private long value;
    
        private boolean compareAndSwap(long expectedValue, long oldValue, long newValue) {
            if(expectedValue == oldValue) {
                value = newValue;
                return true;
            } else return false;
        }
    
        public long increment() {
            long oldValue = value;
            long newValue;
            do {
                newValue = value + 1;
            } while (compareAndSwap(value, oldValue, newValue));
            return value;
        }
    }
    

    3.CAS的ABA问题

    CAS实现原子性的背后有一个假设:如果共享变量的当前值与期望值相同,就假设共享变量没有被更改过。

    但事实可能不是如此:x初始值位0,A将x修改为10,B将x又修改为0,此刻能否认为x没有被更改过呢?这就是CAS的ABA问题。

    如果实际业务需要避免ABA问题,那么我们可以引入一个变量表示版本号,或者称修订号。每进行一次修改,修订号增加1。如果遇到当前版本号与期望版本号不一致,则获取新的版本号并继续修改。此时的过程是这样的。[A, 0] -> [B, 1] -> [C, 2]

    4. Atomic原子类

    原子变量类是基于CAS实现的。当我们对共享变量进行read-modify-write的更新操作时,通过原子变量类可以保障操作的原子性和可见性。

    read-modify-write操作指的是:对于此次操作,变量的新值依赖于变量的旧值。而不是像那种赋值操作。

    前面提到过,volatile只能保障可见性,不能保障原子性。而原子变量类的内部使用的是volatile修饰的变量,并且使用CAS保障了原子性。有时将原子变量类看成是增强的volatile变量。

    分组                                                                 原子变量类
    基础原子类                                         AtomicInteger、AtomicLong、AtomicBoolean
    数组原子类                         AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
    字段更新原子类         AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
    引用原子类                         AtomicReference、AtomicStampedReference、AtomicMarkableReference

    1)AtomicLong示例:

    //我们想要让整个计算过程只使用这一个计算器,所以这里我们将其设计成单例
    public class Indicator {
        //将构造方法私有化
        private Indicator(){}
        //创建一个静态的实例类
        private static Indicator instance = new Indicator();
        //返回上面的那个实例类
        public static Indicator getInstance() {
            return instance;
        }
        //记录请求数
        private AtomicLong requestNum = new AtomicLong(0);
        //记录成功数
        private AtomicLong successNum = new AtomicLong(0);
        //记录失败数
        private AtomicLong failureNum = new AtomicLong(0);
        //请求数增加
        public void requestProcess() {
            requestNum.incrementAndGet();
        }
        //成功数增加
        public void requestProcessSuccess() {
            successNum.incrementAndGet();
        }
        //失败数增加
        public void requestProcessFailure() {
            failureNum.incrementAndGet();
        }
        //获取请求数
        public Long getRequestNum() {
            return requestNum.get();
        }
        //获取成功数
        public Long getRequestSuccessNum() {
            return successNum.get();
        }
        //获取失败数
        public Long getRequestFailureNum() {
            return failureNum.get();
        }
    }
    

    2)AtomicArray示例:

    public class Test {
    
        public static void main(String[] args) {
            //1、创建一个具有指定长度的原子数组
            AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);
            System.out.println(atomicIntegerArray);
            //2、返回指定位置的元素
            System.out.println(atomicIntegerArray.get(0));
            System.out.println(atomicIntegerArray.get(1));
            //3、设置指定位置元素的值
            atomicIntegerArray.set(0, 1);
            System.out.println(atomicIntegerArray.getAndSet(0, 2));  //先获取旧值,再获取新值
            //4、修改某个数组元素的值
            System.out.println(atomicIntegerArray.addAndGet(0, 5));  //先修改,再返回
            System.out.println(atomicIntegerArray.getAndAdd(0, 6));  //先返回,再修改
            //5、CAS操作
            atomicIntegerArray.compareAndSet(0, 13, 222);  //如果0位置的值是22,就修改为222
            System.out.println(atomicIntegerArray.get(0));
            //6、自增/自减
            System.out.println(atomicIntegerArray.incrementAndGet(0));  //先增再获得
            System.out.println(atomicIntegerArray.getAndIncrement(0));  //先获得再增
            System.out.println(atomicIntegerArray.decrementAndGet(0));  //先减再获得
            System.out.println(atomicIntegerArray.getAndDecrement(0));  //先获得再减
        }
    }
    
    

    3)AtomicIntegerFieldUpdater:字段更新器

    AtomicIntegerFieldUpdater可以对原子整数字段进行更新,要求:

    • 字段必须使用volatile修饰,是其在线程间可见。
    • 只能是实例变量,不能是静态变量,也不能用final修饰
    public class SubThread extends Thread{
        //要更新的user对象
        private User user;
        //创建更新器,对user对象的age字段进行更新
        private AtomicIntegerFieldUpdater<User> updater = AtomicIntegerFieldUpdater.newUpdater(User.class, \"age\");
    
        public SubThread(User user) {
            this.user = user;
        }
    
        @Override
        public void run() {
            //在子线程中对user对象的age自增10次
            for(int i = 0; i < 10; i++) {
                updater.incrementAndGet(user);
            }
        }
    }
    

    4)AtomicReference:原子引用对象

    public class Test01 {
    
        //创建一个reference对象
        static AtomicReference<String> atomicReference = new AtomicReference<>(\"abc\");
    
        public static void main(String[] args) {
            for(int i = 0; i < 100; i++) {
                int temp = i;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        atomicReference.set(atomicReference.get() + temp);
                        System.out.println(atomicReference.get());
                    }
                }).start();
            }
        }
    }
    
    

    5)AtomicStampedReference:解决CAS中的ABA问题

    /**
     * AtomicStampedReference原子类可以用来解决CAS中的ABA问题
     * AtomicStampedReference原子类中有一个整数标记值stamp,每次执行CAS操作时,会比较它的版本。
     */
    public class Test01 {
    
        private static AtomicStampedReference<String> atomicStampedReference = new AtomicStampedReference<>(\"abc\", 0);
    
        public static void main(String[] args) throws InterruptedException {
    
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    atomicStampedReference.compareAndSet(\"abc\", \"def\", atomicStampedReference.getStamp(),
                            atomicStampedReference.getStamp()+1);
                    System.out.println(Thread.currentThread().getName() + \": \" + atomicStampedReference.getReference());
                    atomicStampedReference.compareAndSet(\"def\", \"abc\", atomicStampedReference.getStamp(),
                            atomicStampedReference.getStamp()+1);
                }
            });
    
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    int stamp = atomicStampedReference.getStamp();
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(atomicStampedReference.compareAndSet(\"abc\", \"ggg\", stamp,
                            atomicStampedReference.getStamp()+1));
                }
            });
    
            t1.start();
            t2.start();
            t1.join();
            t2.join();
    
            System.out.println(atomicStampedReference.getReference());
        }
    }
    

    5.Unsafe类

    Unsafe 是位于 sun.misc 包下的一个类,Unsafe 提供了CAS 方法,直接通过native 方式(封装 C++代码)调用了底层的 CPU 指令 cmpxchg。
    Unsafe类,翻译为中文:危险的,Unsafe全限定名是 sun.misc.Unsafe,从名字中我们可以看出来这个类对普通程序员来说是“危险”的,一般应用开发者不会用到这个类。

    1、Unsafe 提供的 CAS 方法

    主要如下: 定义在 Unsafe 类中的三个 “比较并交换”原子方法

    /*
    @param o 包含要修改的字段的对象
    @param offset 字段在对象内的偏移量
    @param expected 期望值(旧的值)
    @param update 更新值(新的值)
    @return true 更新成功 | false 更新失败
    */
    public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object update);
    public final native boolean compareAndSwapInt( Object o, long offset, int expected,int update);
    public final native boolean compareAndSwapLong( Object o, long offset, long expected, long update);
    

    Unsafe 提供的 CAS 方法包含四个入参: 包含要修改的字段对象、字段内存位置、预期原值及新值。在执行 Unsafe 的 CAS 方法的时候,这些方法首先将内存位置的值与预期值(旧的值)比较,如果相匹配,那么处理器会自动将该内存位置的值更新为新值,并返回 true ;如果不相匹配,处理器不做任何操作,并返回 false 。

    2、获取属性偏移量

    Unsafe 提供的获取字段(属性)偏移量的相关操作,主要如下:

    /**
    * @param o 需要操作属性的反射 
    * @return 属性的偏移量 
    */ 
    public native long staticFieldOffset(Field field); 
    public native long objectFieldOffset(Field field);
    

    staticFieldOffset 方法用于获取静态属性 Field 在 Class 对象中的偏移量,在 CAS 操作静态属性时,会用到这个偏移量。
    objectFieldOffset 方法用于获取非静态 Field (非静态属性)在 Object 实例中的偏移量,在 CAS 操作对象的非静态属性时,会用到这个偏移量。

    3、根据属性的偏移量获取属性的最新值:

    /**
    * @param o 字段所属于的对象实例
    * @param fieldOffset 字段的偏移量 
    * @return 字段的最新值
    */
    public native int getIntVolatile(Object o, long fieldOffset);
    

    以上就是Java多线程:线程同步详解(CAS、原子类、UnSafe)的全部内容。

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/9682.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

988
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

465
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

348
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

456
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

517
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

832
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号