目录
1. JUC简介:
2. atomic(原子数据类的包)内部实现原理
3. 以 AtomicInteger 为例,内部的CAS实现
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)
为了后面的内容看起来更容易懂,首先在讲之前先补充几个知识点:
接下来开始介绍: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同时更新同一个变量时,只有其中一个线程能更新变量的值,而其他线程都将失败。但是,失败的线程并不会被挂起(这就是与获取锁的机制不同之处),而是被告知在这次竞争中失败,并可以多次尝试。这种灵活性就大大减少了与锁相关的活跃性风险。
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()为例进行说明,执行步骤如下:
其他关于JUC源码解析文章:
深入了解 Java JUC(一)之 atomic(原子数据类的包)
深入了解 Java JUC(二)之 从JUC锁机制AQS到重入锁、读写锁和CountDownLatch
java JUC 之 四种常用线程池 + Spring提供的线程池技术