Java是如何实现CAS的

简介: Java是如何实现CAS的

前言

在java面试中,经常会被问到i++是不是一个原子性操作?,或者以下代码的执行结果是什么?

private volatile static int i = 0;
  public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
      // 1000次循环i++s
      for (int x = 0; x < 1000; x++) {
        i++;
      }
    });
    Thread t2 = new Thread(() -> {
      // 1000次循环i++
      for (int x = 0; x < 1000; x++) {
        i++;
      }
    });
    // 启动线程t1,t2
    t1.start();
    t2.start();
    // 等待t1,t2执行结束
    t1.join();
    t2.join();
    // 打印i的数值
    System.out.println(i);
  }
复制代码

很显然,在大多数情况下,i的值基本都小于2000,且是一个不固定的值。为了解决这个问题,大部分程序员都会提出使用AtomicInteger来解决:

// 使用AtomicInteger包装
private static AtomicInteger i = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
      for (int x = 0; x < 1000; x++) {
        // 自增
        i.getAndIncrement();
      }
    });
    Thread t2 = new Thread(() -> {
      for (int x = 0; x < 1000; x++) {
        // 自增
        i.getAndIncrement();
      }
    });
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    // 打印i的值
    System.out.println(i.get()); 
}
复制代码

通过上述代码修改后,我们得到的i的值一定是自增2000次后的结果。那么为什么AtomicInteger包装一下后就能满足原子性呢?

CAS的执行流程

为了解答我们的疑问,我们需要简单地看一下AtomicInteger的源码:

public final int getAndIncrement() {
    return U.getAndAddInt(this, VALUE, 1);
}
复制代码

好吧,就一行代码,确实看不出什么原理,只能再进一步深入:

@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);
    } while (!weakCompareAndSetInt(o, offset, v, v + delta));
    return v;
}
复制代码

我们可以发现getIntVolatile()是一个native方法,这个方法是由底层的C++语言实现的;但是通过两个参数的含义基本可以猜测到:getIntVolatile()将会从对象o的相对偏移量offset取值,那么这个取出来的值就是内存中的数值v;

@HotSpotIntrinsicCandidate
public final boolean weakCompareAndSetInt(Object o, long offset, int expected, int x) {
    return compareAndSetInt(o, offset, expected, x);
}
复制代码

另外,我们发现compareAndSetInt()也是native方法,不过根据源码注释我们依然还是可以看明白这个方法的意思:对比对象o偏移量offset位置的值,看看是不是和expected一致,如果一致的话,就把偏移量offset位置的值修改为x,并且compareAndSetInt()这个操作是原子性的。

那么根据以上两段代码的解析,我们就可以大致分析出CAS的执行流程了:

1.先通过对象的偏移量找到目标值;

2.判断找到的目标值是否和内存中偏移量位置的值一致(为避免在执行间隙有其他线程执行导致内存值产生变化,所以要时刻保持与内存值比较),如果一致,说明这个时间差内没有其他线程修改内存值,那么就可以放心地把内存值改成我们的目标值x

3.如果修改失败,那么说明内存值产生了变化,那么重新执行步骤1;

以上就是我们根据源码分析总结出来的CAS的执行流程,那么我们还有一个疑问,对象的偏移量是怎么计算出来的呢?

CAS的实现原理(基石Unsafe)

AtomicInteger类中,我们会看到这样三段代码:

// Unsafe对象,只能在Boostrap ClassLoader下才能被创建
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
// 字段value在AtomicInteger对象中的偏移量
private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
// 被包装的目标字段
private volatile int value;
复制代码

在java中,每一个字段在内存中都会占据一定大小的位置,int类型的也是一样。Unsafe对象就是根据这个原理,通过计算这个字段在类中的相对偏移量,那么在对象被创建出来后,就能够根据这个早就计算好的相对偏移量来直接获取指定字段在内存中的值了。

通过以上分析,我们很自然地就知道了,原来CAS的实现是基于Unsafe能够获取字段在对象中的相对偏移量,并通过不断地自旋操作比较内存值是否发生变化来决定是否更改内存中的目标值。


相关文章
|
Java 编译器
解密Java多线程中的锁机制:CAS与Synchronized的工作原理及优化策略
解密Java多线程中的锁机制:CAS与Synchronized的工作原理及优化策略
|
3月前
|
安全 Java API
JAVA并发编程JUC包之CAS原理
在JDK 1.5之后,Java API引入了`java.util.concurrent`包(简称JUC包),提供了多种并发工具类,如原子类`AtomicXX`、线程池`Executors`、信号量`Semaphore`、阻塞队列等。这些工具类简化了并发编程的复杂度。原子类`Atomic`尤其重要,它提供了线程安全的变量更新方法,支持整型、长整型、布尔型、数组及对象属性的原子修改。结合`volatile`关键字,可以实现多线程环境下共享变量的安全修改。
|
4天前
|
安全 算法 Java
Java CAS原理和应用场景大揭秘:你掌握了吗?
CAS(Compare and Swap)是一种乐观锁机制,通过硬件指令实现原子操作,确保多线程环境下对共享变量的安全访问。它避免了传统互斥锁的性能开销和线程阻塞问题。CAS操作包含三个步骤:获取期望值、比较当前值与期望值是否相等、若相等则更新为新值。CAS广泛应用于高并发场景,如数据库事务、分布式锁、无锁数据结构等,但需注意ABA问题。Java中常用`java.util.concurrent.atomic`包下的类支持CAS操作。
23 2
|
4月前
|
安全 Java 调度
解锁Java并发编程高阶技能:深入剖析无锁CAS机制、揭秘魔法类Unsafe、精通原子包Atomic,打造高效并发应用
【8月更文挑战第4天】在Java并发编程中,无锁编程以高性能和低延迟应对高并发挑战。核心在于无锁CAS(Compare-And-Swap)机制,它基于硬件支持,确保原子性更新;Unsafe类提供底层内存操作,实现CAS;原子包java.util.concurrent.atomic封装了CAS操作,简化并发编程。通过`AtomicInteger`示例,展现了线程安全的自增操作,突显了这些技术在构建高效并发程序中的关键作用。
77 1
|
7月前
|
算法 安全 Java
Java多线程基础-12:详解CAS算法
CAS(Compare and Swap)算法是一种无锁同步原语,用于在多线程环境中更新内存位置的值。
74 0
|
5月前
|
安全 Oracle Java
(四)深入理解Java并发编程之无锁CAS机制、魔法类Unsafe、原子包Atomic
其实在我们上一篇文章阐述Java并发编程中synchronized关键字原理的时候我们曾多次谈到过CAS这个概念,那么它究竟是什么?
128 1
|
7月前
|
安全 Java 编译器
Java 多线程系列Ⅴ(常见锁策略+CAS+synchronized原理)
Java 多线程系列Ⅴ(常见锁策略+CAS+synchronized原理)
|
7月前
|
算法 Java
Java中CAS算法的集中体现:Atomic原子类库,你了解吗?
【5月更文挑战第15天】Java中CAS算法的集中体现:Atomic原子类库,你了解吗?
57 1
|
6月前
|
安全 Java
并发编程-Java如何实现原子操作(CAS或锁)
并发编程-Java如何实现原子操作(CAS或锁)
41 0
|
7月前
|
存储 安全 Java
【Java EE】CAS原理和实现以及JUC中常见的类的使用
【Java EE】CAS原理和实现以及JUC中常见的类的使用