Unsafe 和 Atomic 详解(上)

简介: 在JDK 5之后,Java类库中才开始使用CAS操作,该操作由sun.misc.Unsafe类里面的 compareAndSwapInt()和compareAndSwapLong()等几个方法包装提供。HotSpot虚拟机在内部对这些方法做了特殊处理,即时编译出来的结果就是一条平台相关的处理器CAS指令,没有方法调用的过程, 或者可以认为是无条件内联进去了。

在JDK 5之后,Java类库中才开始使用CAS操作,该操作由sun.misc.Unsafe类里面的 compareAndSwapInt()和compareAndSwapLong()等几个方法包装提供。HotSpot虚拟机在内部对这些方法做了特殊处理,即时编译出来的结果就是一条平台相关的处理器CAS指令,没有方法调用的过程, 或者可以认为是无条件内联进去了。不过由于Unsafe类在设计上就不是提供给用户程序调用的类(Unsafe::getUnsafe()的代码中限制了只有启动类加载器(Bootstrap ClassLoader)加载的Class才能访问它),因此在JDK 9之前只有Java类库可以使用CAS,譬如J.U.C包里面的整数原子类,其中的 compareAndSet()和getAndIncrement()等方法都使用了Unsafe类的CAS操作来实现。而如果用户程序也有用CAS操作的需求,那要么就采用反射手段突破Unsafe的访问限制,要么就只能通过Java类库API来间接使用它。直到JDK 9之后,Java类库才在VarHandle类里开放了面向用户程序使用的CAS操作。


Unsafe 实现 CAS


核心 API


  1. compareAndSwapObject


  1. compareAndSwapInt


  1. compareAndSwapInt


原子变量提供的原子性来自CAS操作,CAS来自Unsafe,然后由CPU的 cmpxchg 指令来保证。


Atomic 工具包


Atomic 工具类分为一下几类


  • 普通原子类型:提供对boolean、int、long和对象的原子性操作。


  • AtomicBoolean
  • AtomicInteger
  • AtomicLong
  • AtomicReference


  • 原子类型数组:提供对数组元素的原子性操作。


  • AtomicLongArray
  • AtomicIntegerArray
  • AtomicReferenceArray


  • 原子类型字段更新器:提供对指定对象的指定字段进行原子性操作。


  • AtomicLongFieldUpdater
  • AtomicIntegerFieldUpdater
  • AtomicReferenceFieldUpdater


  • 带版本号的原子引用类型:以版本戳的方式解决原子类型的ABA问题。


  • AtomicStampedReference
  • AtomicMarkableReference


  • 原子累加器(JDK1.8):AtomicLong和AtomicDouble的升级类型,专门用于数据统计,性能更高。


  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder


使用案例


多线程累加


下面是创建 1000 个线程分别对 total 进行累加,由于没有对 total 做任何线程安全操作所以我们打印的结果一定是小于或者等于 1000000


public class AtomicTest {
    public static int total = 0;
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch downLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    total++;
                }
                downLatch.countDown();
            }).start();
        }
        downLatch.await();
        System.out.println(total);
    }
}
// 结果输出
// 999843


通过 AtomicInteger 实现


我们可以通过 integer 的原子类,AtomicInteger 类来进行自增操作,然后调用 getAndIncrement() 方法,保证每次都是原子操作。最终的结果输出:1000000。


public class AtomicTest2 {
    public static AtomicInteger total = new AtomicInteger(0);
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch downLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    total.getAndIncrement();
                }
                downLatch.countDown();
            }).start();
        }
        downLatch.await();
        System.out.println(total.get());
    }
}
// 结果输出
// 1000000


再来看看 getAndIncrement 方法它主要是做了哪些操作呢 ?


public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
// 然后调用 unsafe 类的本地方法
// unsafe.getAndAddInt 方法
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;
}


目录
打赏
0
0
0
0
5
分享
相关文章
并发编程之原子操作Atomic&Unsafe
并发编程之原子操作Atomic&Unsafe
C++ 原子变量atomic variable
原子变量是 C++11 引入的一种同步机制,用于多线程环境中的无锁、线程安全操作。其操作不可分割,避免了数据竞争和不一致问题。原子变量位于 `&lt;atomic&gt;` 头文件中,支持多种类型如 `std::atomic&lt;T&gt;` 和特化类型。基本用法包括定义原子变量、加载、存储、交换及比较交换操作。内存顺序(如 `std::memory_order_seq_cst`)用于控制内存访问顺序和可见性,适用于不同场景。原子变量常用于线程安全的计数器和标志位等。
|
8月前
|
C++
C++11 std::lock_guard 互斥锁
C++11 std::lock_guard 互斥锁
72 0
|
5月前
|
Java多线程编程:使用Atomic类实现原子操作
在Java多线程环境中,共享资源的并发访问可能导致数据不一致。传统的同步机制如`synchronized`关键字或显式锁虽能保障数据一致性,但在高并发场景下可能导致线程阻塞和性能下降。为此,Java提供了`java.util.concurrent.atomic`包下的原子类,利用底层硬件的原子操作确保变量更新的原子性,实现无锁线程安全。
47 0
|
8月前
|
C++标准库中的锁lock_guard、unique_lock、shared_lock、scoped_lock、recursive_mutex
C++标准库中的锁lock_guard、unique_lock、shared_lock、scoped_lock、recursive_mutex
286 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等