Java无锁系列——2、CAS 实现原理 Unsafe 类简单介绍

本文详细解析了Java中CAS的实现原理,包括Unsafe类的介绍,以及如何利用Unsafe进行基础类型、对象引用、数组和对象属性的CAS更新。重点探讨了Unsafe的native方法在内存操作中的作用,确保了无锁同步的安全性。
摘要由CSDN通过智能技术生成

概述

在上篇博客中,我简单介绍了无锁同步 CAS 如何使用以及部分它的特性。本篇我打算整理一下 CAS 的实现原理即 Unsafe 类相关的知识。


Unsafe

本篇博客分以下几个模块展开:

  1. Unsafe 类简单介绍
  2. CAS 更新基础类型原理
  3. CAS 更新对象引用原理
  4. CAS 更新数组类型原理
  5. CAS 更新对象属性原理
  6. Unsafe 类方法总结
  7. Unsafe 类示例

1、Unsafe 类简单介绍

Unsafe 类处于包 sun.misc 下,该包由 sun 公司内部实现,不属于 J2EE 开发规范。

java 代码中任何 CAS 操作最终都是通过调用 Unsafe 类中的 native 方式实现,也就是说:Unsafe 通过调用操作系统底层资源实现任务。

从名称就可以看出,Unsafe 类是不安全的。它可以像C语言指针那样直接操作内存。一般我们不建议直接使用 Unsafe 类处理任务。


2、CAS 更新基础类型原理

上篇博客我们提到 CAS 可以更新基础类型数据,这里我们就拿 AtomicInteger 类的源码进行分析:

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;

public final boolean compareAndSet(int expect, int update) {
   
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

通过上面的源码,我们可以看出:AtomicInteger 类的 CAS 方法最终还是调用了 Unsafe 类的方法。Unsafe 类方法的源码如下所示:

public native long objectFieldOffset(Field var1);

@CallerSensitive
public static Unsafe getUnsafe() {
   
    Class var0 = Reflection.getCallerClass();
    if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
   
        throw new SecurityException("Unsafe");
    } else {
   
        return theUnsafe;
    }
}

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

首先通过 Reflection.getCallerClass() 可以表明,调用此方法必须包含以下权限:

  • bootstrap class loader 加载的类可以调用
  • extension class loader 加载的类可以调用

而我们用户编写的类都是通过 application class loader 加载的,也就是说用户编写的代码不能通过这个静态方法直接获取对象实例,并且 Unsafe 类本身也没有提供公开的构造方法。

这样做的原因也非常明显:防止用户直接使用 Unsafe 类。然而事实上,只要愿意的话,同样可以通过反射创建该对象实例,相应出现的风险也需要程序员自己承担。

下面我们再看 AtomicInteger 对象调用 Unsafe 类方法时所传递的参数:

  • this:对象本身
  • valueOffset:AtomicInteger 对象 value 属性偏移地址
  • expect:期望值
  • update:新值

也就是说,AtomicInteger 首先根据 objectFieldOffset() 方法确定 value 属性在对象上的偏移值。然后将对象本身,偏移值,期望值,新值作为参数传递过去。在 compareAndSwapInt() 方法中:首先根据对象确定内存,然后根据偏移值获取到要操作的内存地址,直接拿期望值和内存中的值做比较,如果相等的话就将新值写入内存。

从这里也就可以看出 Unsafe 类直接通过操作内存完成 CAS 操作,调用的方法本身又是 native 方法,因此操作本身就是原子的,也就是说不会出现线程安全问题。

有了上面的铺垫,我们再来看另一个 AtomicInteger 常用的方法源码:

AtomicInteger 源码:

public final int getAndIncrement() {
   
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

Unsafe 源码:

public final int getAndAddInt(Object var1, long var2, int var4) {
   
   int var5;
   do {
   
       var5 = this.getIntVolatile(var1, var2);
   } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

   return var5;
}

public native int getIntVolatile(Object var1, long var2)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值