J.U.C 学习(四)之 “Atomic原子操作类”

前言

原子性这个概念,在多线程编程里是一个老生常谈的问题。 所谓的原子性表示一个或者多个操作,要么全部执行完,要么一个也不执行。不能出现一部分成功,一部分失败的情况。
在多线程中,如果多个线程同时更新一个共享变量,可能会得到一个意料之外的值。比如 i=1 。A 线程更新 i+1 、 B 线程也更新 i+1。
通过两个线程并行操作之后可能 i 的值不等于 3。而可能等 于 2。因为 A 和 B 在更新变量 i 的时候拿到的 i 可能都是 1 这就是一个典型的原子性问题
多线程里面,要实现原子性,有几种方法,其中一种就是加 Synchronized 同步锁。
而从 JDK1.5 开始,在 J.U.C 包中提供了 Atomic 包,提供了对于常用数据结构的原子操作。它提供了简单、高效、以及线程安全的更新一个变量的方式

J.U.C 中的原子操作类

由于变量类型的关系,在 J.U.C 中提供了 12 个原子操作的类。这 12 个类可以分为四大类

  • 原子更新基本类型
    1. AtomicBoolean
    2. AtomicInteger
    3. AtomicLong
  • 原子更新数组
    1. AtomicIntegerArray
    2. AtomicLongArray
    3. AtomicReferenceArray
  • 原子更新引用
    1. AtomicReference
    2. AtomicReferenceFieldUpdater
    3. AtomicMarkableReference(更新带有标记位的引用类 型)
  • 原子更新字段
    1. AtomicIntegerFieldUpdater
    2. AtomicLongFieldUpdater
    3. AtomicStampedReference

AtomicInteger 原理分析

atomicInteger 主要方法剖析

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

getAndIncrement 实际上是调用 unsafe 这个类里面提供的方法,
Unsafe 类我们前面在分析 AQS 的时候讲过,这个类相当于是一个后门,使得 Java 可以像 C 语言的指针一样直接操作内存空间。当然也会带来一些弊端,就是指针的问题。 实际上这个类在很多方面都有使用,除了 J.U.C 这个包以外,还有 Netty、kafka 等等

这个类提供了很多功能,包括多线程同步(monitorEnter)、CAS 操作(compareAndSwap)、线程的挂起和恢复(park/unpark)、内存屏障(loadFence/storeFence)内存管理(内存分配、释放内存、获取内存地址等.)

  • valueOffset
private static final long valueOffset;

static {
    
    
    try {
    
    
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) {
    
     throw new Error(ex); }
}

通过 unsafe.objectFieldOffset() 获取当前 Value 这个变量在内存中的偏移量,后续会基于这个偏移量从内存中得到 value 的值来和当前的值做比较, 实现乐观锁

  • getAndAdd jdk1.7 和jdk1.8的实现方式不同 (getAndAddInt 为 unsafe.class 中的方法)
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;
}

通过 do/while 循环,基于 CAS 乐观锁来做原子递增。实际上前面的 valueOffset 的作用就是从主内存中获得当前 value 的值和预期值做一个比较,如果相等,对 value 做递增并结束循环

  • get 方法
private volatile int value;

public final int get() {
    
    
    return value;
}

get 方法只需要直接返回 value 的值就行,这里的 value 是通过 Volatile 修饰的,用来保证可见性

  • 其他方法
    AtomicInteger 的实现非常简单,所以我们可以很快就分析完它的实现原理,当然除了刚刚分析的这两个方法之外, 还有其他的一些。比如它提供了 compareAndSet,允许客户端基于 AtomicInteger 来实现乐观锁的操作
public final boolean compareAndSet(int expect, int update) {
    
    
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

下一篇
J.U.C 学习(五)之 “JUC其他常用并发工具”

猜你喜欢

转载自blog.csdn.net/nonage_bread/article/details/110933775
今日推荐