深入了解 Java JUC(一)之 atomic(原子数据类的包)

目录

1. JUC简介:

2. atomic(原子数据类的包)内部实现原理

3. 以 AtomicInteger 为例,内部的CAS实现


1. JUC简介:

        JUC 是 java.util.concurrent 的简称,在 Java 5.0 提供了 java.util.concurrent(简称JUC)包,这个包包含了一系列能够让 Java 的并发编程变得更加简单轻松的工具类,在这之前,你需要自己手动去实现相关的工具类。JUC包主要包括五部分内容:

1. atomic(原子数据类的包)

2. locks (Java 锁机制---注意区别于synchronized 关键字)

3. collections (提供并发集合框架)

4. executor (线程任务的执行者)

5. tools (辅助工具包 CountDownLatch,CyclicBarrier,Exchanger)

2. atomic(原子数据类的包)内部实现原理

 

        为了后面的内容看起来更容易懂,首先在讲之前先补充几个知识点:

  •         从JVM 编译后的指令解释:为什么 i++ 不是原子操作?https://blog.csdn.net/weixin_38497019/article/details/99677684
  •         Java 并发编程之 volatile 关键字:https://blog.csdn.net/weixin_38497019/article/details/99430092

        接下来开始介绍:atomic是原子类的包,在源码里,atomic自己就是一个包,支持对单个变量进行无锁线程的安全编辑, 如AtomicInteger,修饰单个变量,可以实现int类型原子的加。

        本质上,它扩展了volatile的概念并且提供了一个方法:

boolean compareAndSet(expectedValue,  updateValue);

        这个方法底层用CAS(compare and swap算法)实现。

 CAS 算法介绍:

  • CAS(Compare-And-Swap) 算法是硬件对于并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问;
  • CAS 是一种无锁的非阻塞算法的实现;
  • CAS 包含了三个操作数(V, A, B):

-- 需要读写的内存值: V

-- 进行比较的预估值: A

-- 拟写入的更新值: B

-- 当且仅当 V == A 时, V = B, 否则,将不做任何操作;

        简单来说,CAS的含义是:“如果在修改V的值的时候,首先我认为V的值应该是A,如果是,那么将V的值更新为B,如果此时V的值不是A,那么不做修改并告诉V的值实际为多少”。

        CAS是一项乐观的技术,它希望能成功地执行更新操作,并且如果有另一个线程在最近一次检查后更新了该变量,那么CAS能检测到这个错误。当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其他线程都将失败。但是,失败的线程并不会被挂起(这就是与获取锁的机制不同之处),而是被告知在这次竞争中失败,并可以多次尝试。这种灵活性就大大减少了与锁相关的活跃性风险。

3. 以 AtomicInteger 为例,内部的CAS实现

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // valueOffset 为value的偏移地址(此注释是我自己加的,源码中没有这个注释)
    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 AtomicInteger(int initialValue) {
        value = initialValue;
    }

    public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
}

说明: 从源码中就可以看到AtomicInteger内部都是使用了Unsafe类来进行CAS操作,valueOffset表示的是value值的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的原值的, 偏移量可以简单理解为指针指向该变量的内存地址。 value使用volatile关键字修饰,直接从共享内存中操作变量,保证多线程之间看到的value值是同一份。

      我们以方法getAndUpdate()为例进行说明,执行步骤如下:

  1. 从内存中读取修改前的值prev,并执行给定函数式计算修改后的值next;
  2. 调用compareAndSet修改value值(内部是调用了unsafe的compareAndSwapInt方法)。如果此时有其他线程也在修改这个value值,那么CAS操作就会失败,继续进入do循环重新获取新值,再次执行CAS直到修改成功。

 

其他关于JUC源码解析文章:

深入了解 Java JUC(一)之 atomic(原子数据类的包)

深入了解 Java JUC(二)之 从JUC锁机制AQS到重入锁、读写锁和CountDownLatch

java JUC 之 四种常用线程池 + Spring提供的线程池技术

你可能感兴趣的:(深入了解,Java,JUC)