在本系列内容中我们会对JUC做一个系统的学习,本片将会介绍JUC的无锁
我们会分为以下几部分进行介绍:
这一小节我们将讲解如何用无锁操作完成并发操作
我们给出一段之前并发展示代码:
- /*并发代码*/
-
- package cn.itcast;
- import java.util.ArrayList;
- import java.util.List;
- interface Account {
- // 获取余额
- Integer getBalance();
- // 取款
- void withdraw(Integer amount);
- /**
- * 方法内会启动 1000 个线程,每个线程做 -10 元 的操作
- * 如果初始余额为 10000 那么正确的结果应当是 0
- */
- static void demo(Account account) {
- List<Thread> ts = new ArrayList<>();
- long start = System.nanoTime();
- for (int i = 0; i < 1000; i++) {
- ts.add(new Thread(() -> {
- account.withdraw(10);
- }));
- }
- ts.forEach(Thread::start);
- ts.forEach(t -> {
- try {
- t.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- });
- long end = System.nanoTime();
- System.out.println(account.getBalance()
- + " cost: " + (end-start)/1000_000 + " ms");
- }
- }
-
- /*主代码*/
- public static void main(String[] args) {
- Account.demo(new AccountUnsafe(10000));
- }
-
- /*输出结果*/
- 330 cost: 306 ms
我们在之前已经学习过了锁的基本操作,并且可以解决并发问题:
- /*并发代码*/
-
- // 给 Account 对象加锁
- class AccountUnsafe implements Account {
- private Integer balance;
- public AccountUnsafe(Integer balance) {
- this.balance = balance;
- }
- @Override
- public synchronized Integer getBalance() {
- return balance;
- }
- @Override
- public synchronized void withdraw(Integer amount) {
- balance -= amount;
- }
- }
-
- /*主代码*/
- public static void main(String[] args) {
- Account.demo(new AccountUnsafe(10000));
- }
-
- /*输出结果*/
- 0 cost: 399 ms
JDK为我们提供了几种乐观锁的无锁并发问题解决类型:
- /*解释*/
- AtomicInteger:原子int类型,属于实现类,传入一个integer类型的参数,可以调用其内部方法对其改变
- AtomicInteger内部有一个value值,该值会存放你传入的Integer参数,其所有方法都是对该值进行改变或获得!
-
- /*并发代码*/
- class AccountSafe implements Account {
- // 定义共享数据为乐观锁AtomicInteger类型
- private AtomicInteger balance;
-
- // 构造方法,在创建类时,将传入的参数创建为AtomicInteger类型并赋值
- public AccountSafe(Integer balance) {
- this.balance = new AtomicInteger(balance);
- }
-
- // 获得:调用AtomicInteger类型的get方法
- @Override
- public Integer getBalance() {
- return balance.get();
- }
-
- // 改变:amount为值
- @Override
- public void withdraw(Integer amount) {
- // 一直进行直到完成操作
- while (true) {
- // 记录修改前的值和修改后的值
- int prev = balance.get();
- int next = prev - amount;
- // 采用compareAndSet,首先对比当前值是否为prev,如果是将该值修改为next,并返回true
- if (balance.compareAndSet(prev, next)) {
- // 执行成功,退出循环!
- break;
- }
- }
- // 可以简化为下面的方法
- // balance.addAndGet(-1 * amount);
- }
- }
-
- /*主代码*/
- public static void main(String[] args) {
- Account.demo(new AccountSafe(10000));
- }
-
- /*运行结果*/
- 0 cost: 302 ms
-
- /*补充说明*/
- 乐观锁并没有运用锁,它采用的是不断运行,如果可以执行就执行,如果不可以执行就一直运行直到执行成功!
这一小节我们将讲解无锁操作中的CAS和Volatile相关内容
首先我们介绍一下CAS:
此外我们还需要知道CAS本身就是原子操作:
其实 CAS 的底层是 lock cmpxchg 指令(X86 架构),在单核 CPU 和多核 CPU 下都能够保证【比较-交换】的原子性。
在多核状态下,某个核执行到带 lock 的指令时,CPU 会让总线锁住,当这个核把此指令执行完毕,再 开启总线。
这个过程中不会被线程的调度机制所打断,保证了多个线程对内存操作的准确性,是原子的。
我们给出一张CAS操作的展示图:

我们在之前已经详细的介绍了Volatile的内容:
CAS必须搭配Volatile共同使用:
我们来简单介绍一下CAS的特点:
我们反观Synchronized的特点:
因而我们其实可以很清楚的明白无锁操作是要比锁操作速度要快的:
但是也有特殊状况:
这一小节我们将讲解无锁操作中的各种原子类型
首先我们来介绍一下原子整数,大致分为三类:
由于三种原子整数相似,我们仅给出一种实例:
- /*原子整数型介绍*/
-
- - AtomicBoolean:布尔类型的原子整数
- - AtomicInteger:int类型的原子整数
- - AtomicLong:Long类型的原子整数
-
- /*相关方法展示*/
-
- // 首先创建一个AtomicInteger类型
- AtomicInteger i = new AtomicInteger(0);
-
- // 获取并自增(i = 0, 结果 i = 1, 返回 0),类似于 i++
- System.out.println(i.getAndIncrement());
-
- // 自增并获取(i = 1, 结果 i = 2, 返回 2),类似于 ++i
- System.out.println(i.incrementAndGet());
-
- // 自减并获取(i = 2, 结果 i = 1, 返回 1),类似于 --i
- System.out.println(i.decrementAndGet());
-
- // 获取并自减(i = 1, 结果 i = 0, 返回 1),类似于 i--
- System.out.println(i.getAndDecrement());
-
- // 获取并加值(i = 0, 结果 i = 5, 返回 0)
- System.out.println(i.getAndAdd(5));
-
- // 加值并获取(i = 5, 结果 i = 0, 返回 0)
- System.out.println(i.addAndGet(-5));
-
- // 获取并更新(i = 0, p 为 i 的当前值, 结果 i = -2, 返回 0)
- // 其中函数中的操作能保证原子,但函数需要无副作用
- System.out.println(i.getAndUpdate(p -> p - 2));
-
- // 更新并获取(i = -2, p 为 i 的当前值, 结果 i = 0, 返回 0)
- // 其中函数中的操作能保证原子,但函数需要无副作用
- System.out.println(i.updateAndGet(p -> p + 2));
-
- // 获取并计算(i = 0, p 为 i 的当前值, x 为参数1, 结果 i = 10, 返回 0)
- // 其中函数中的操作能保证原子,但函数需要无副作用
- // getAndUpdate 如果在 lambda 中引用了外部的局部变量,要保证该局部变量是 final 的
- // getAndAccumulate 可以通过 参数1 来引用外部的局部变量,但因为其不在 lambda 中因此不必是 final
- System.out.println(i.getAndAccumulate(10, (p, x) -> p + x));
-
- // 计算并获取(i = 10, p 为 i 的当前值, x 为参数1, 结果 i = 0, 返回 0)
- // 其中函数中的操作能保证原子,但函数需要无副作用
- System.out.println(i.accumulateAndGet(-10, (p, x) -> p + x));
-
- /*相关知识补充*/
-
- getAndUpdate,updateAndGet,getAndAccumulate,accumulateAndGet所需的参数都是IntUnaryOperator类型
-
- 该类型只有一个抽象方法,其需要输入int类型,会返回类型,我们只需要采用Lambda表达式重新构造即可使用!
-
- /*源码展示*/
-
- // 以上方法都是以CAS为基础进行了封装,保证了方法的原子性和变量的可见性。
-
- // 我们调其中一个方法进行底层代码的剖析:
- public static int updateAndGet(AtomicInteger i, IntUnaryOperator operator){
- while (true){
- int prev = i.get();
- int next = operator.applyAsInt(prev);
- if(i.compareAndSet(prev,next)){
- return next;
- }
- }
- }
我们的原子引用主要分为三类:
我们针对三种不同的原子引用类型展开讲解:
- /*基本解释*/
-
- - AtomicReference :最基本的原子引用,引用对象后,针对其方法进行改变即可
- - AtomicMarkableReference :在AtomicReference的基础上,多了一个版本号,用来检测当前版本与当时未修改前的版本的差距
- - AtomicStampedReference :在AtomicReference的基础上,多一个判断,用来判断该对象是否被修改
-
- /*不安全版本*/
-
- class DecimalAccountUnsafe implements DecimalAccount {
-
- BigDecimal balance;
-
- public DecimalAccountUnsafe(BigDecimal balance) {
- this.balance = balance;
- }
-
- @Override
- public BigDecimal getBalance() {
- return balance;
- }
-
- @Override
- public void withdraw(BigDecimal amount) {
- BigDecimal balance = this.getBalance();
- this.balance = balance.subtract(amount);
- }
- }
-
- /*锁*/
-
- class DecimalAccountSafeLock implements DecimalAccount {
-
- private final Object lock = new Object();
-
- BigDecimal balance;
-
- public DecimalAccountSafeLock(BigDecimal balance) {
- this.balance = balance;
- }
-
- @Override
- public BigDecimal getBalance() {
- return balance;
- }
-
- @Override
- public void withdraw(BigDecimal amount) {
- synchronized (lock) {
- BigDecimal balance = this.getBalance();
- this.balance = balance.subtract(amount);
- }
- }
- }
-
- /*AtomicReference*/
-
- class DecimalAccountSafeCas implements DecimalAccount {
-
- // 我们并不是直接获得对象本身,而是采用一个AtomicReference类进行包装,里面装的是BigDecimal类型的value值
- // 同样我们后续的操作都是针对这个value值操作
- AtomicReference<BigDecimal> ref;
-
- // 初始化进行赋值
- public DecimalAccountSafeCas(BigDecimal balance) {
- ref = new AtomicReference<>(balance);
- }
-
- // 得到value值
- @Override
- public BigDecimal getBalance() {
- return ref.get();
- }
-
- // 采用compareAndSet方法对value值进行判断并修改
- // 但这时我们只能根据其prev来进行判断,prev和当前值相同,改为next;若不同不修改;无法判断内部是否经过改变
- // 比如,最开始为a,后面有一个线程a->b,又出现一个线程b->a,当然我们当前线程就会默认没有发生变化而直接改变为next目标值
- @Override
- public void withdraw(BigDecimal amount) {
- while (true) {
- BigDecimal prev = ref.get();
- BigDecimal next = prev.subtract(amount);
- if (ref.compareAndSet(prev, next)) {
- break;
- }
- }
- }
- }
-
- /*AtomicMarkableReference */
-
- // 模拟操作
- @Slf4j
- public class TestABAAtomicMarkableReference {
- public static void main(String[] args) throws InterruptedException {
-
- // 首先创建一个垃圾袋装满垃圾
- GarbageBag bag = new GarbageBag("装满了垃圾");
-
- // 我们采用AtomicMarkableReference封装对象,对象为垃圾袋;参数2 mark 可以看作一个标记,表示垃圾袋满了
- AtomicMarkableReference<GarbageBag> ref = new AtomicMarkableReference<>(bag, true);
-
- // 开始操作(打印当前状况)
- log.debug("主线程 start...");
- GarbageBag prev = ref.getReference();
- log.debug(prev.toString());
-
- // 该线程负责打扫垃圾,这时我们会调用AtomicMarkableReference的compareAndSet方法
- // 里面不仅包含了value的prev和next还包含了mark的prev和next
- new Thread(() -> {
- log.debug("打扫卫生的线程 start...");
- bag.setDesc("空垃圾袋");
- while (!ref.compareAndSet(bag, bag, true, false)) {}
- log.debug(bag.toString());
- }).start();
-
- // 在前面的线程修改之后,我们如果还想判断修改,这时需要prev为true,但已经改变为false,所以不会执行
- Thread.sleep(1000);
- log.debug("主线程想换一只新垃圾袋?");
- boolean success = ref.compareAndSet(prev, new GarbageBag("空垃圾袋"), true, false);
- log.debug("换了么?" + success);
- log.debug(ref.getReference().toString());
- }
- }
-
- // 模拟垃圾袋
- class GarbageBag {
- String desc;
- public GarbageBag(String desc) {
- this.desc = desc;
- }
- public void setDesc(String desc) {
- this.desc = desc;
- }
- @Override
- public String toString() {
- return super.toString() + " " + desc;
- }
- }
-
- // 结果展示
- 2019-10-13 15:30:09.264 [main] 主线程 start...
- 2019-10-13 15:30:09.270 [main] cn.itcast.GarbageBag@5f0fd5a0 装满了垃圾
- 2019-10-13 15:30:09.293 [Thread-1] 打扫卫生的线程 start...
- 2019-10-13 15:30:09.294 [Thread-1] cn.itcast.GarbageBag@5f0fd5a0 空垃圾袋
- 2019-10-13 15:30:10.294 [main] 主线程想换一只新垃圾袋?
- 2019-10-13 15:30:10.294 [main] 换了么?false
- 2019-10-13 15:30:10.294 [main] cn.itcast.GarbageBag@5f0fd5a0 空垃圾袋
-
- /*AtomicStampedReference */
-
- // 代码展示
-
- // 我们同样采用AtomicStampedReference来装载对象,但是AtomicStampedReference会多一个版本号
- // 该版本号可以进行++修改,这样我们就可以得知我们进行了几次修改
- static AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);
-
- public static void main(String[] args) throws InterruptedException {
- log.debug("main start...");
- // 获取值 A
- String prev = ref.getReference();
- // 获取版本号
- int stamp = ref.getStamp();
- log.debug("版本 {}", stamp);
- // 如果中间有其它线程干扰,发生了 ABA 现象
- other();
- sleep(1);
- // 尝试改为 C
- log.debug("change A->C {}", ref.compareAndSet(prev, "C", stamp, stamp + 1));
- }
-
- private static void other() {
-
- new Thread(() -> {
- log.debug("change A->B {}", ref.compareAndSet(ref.getReference(), "B",
- ref.getStamp(), ref.getStamp() + 1));
- log.debug("更新版本为 {}", ref.getStamp());
- }, "t1").start();
-
- sleep(0.5);
-
- new Thread(() -> {
- log.debug("change B->A {}", ref.compareAndSet(ref.getReference(), "A",
- ref.getStamp(), ref.getStamp() + 1));
- log.debug("更新版本为 {}", ref.getStamp());
- }, "t2").start();
-
- }
-
- // 结果展示
- 15:41:34.891 c.Test36 [main] - main start...
- 15:41:34.894 c.Test36 [main] - 版本 0
- 15:41:34.956 c.Test36 [t1] - change A->B true
- 15:41:34.956 c.Test36 [t1] - 更新版本为 1
- 15:41:35.457 c.Test36 [t2] - change B->A true
- 15:41:35.457 c.Test36 [t2] - 更新版本为 2
- 15:41:36.457 c.Test36 [main] - change A->C false
我们的原子引用主要分为三类:
这三种数组除了内部包含的元素不同外基本相同,所以我们仅介绍一种:
- /*lambda知识点补充*/
-
- 我们将Lambda里面的类型分为三种类型:
-
- supplier 提供者 无中生有 ()->结果
- function 函数 一个参数一个结果 (参数)->结果;BiFunction (参数1,参数2)->结果
- consumer 消费者 一个参数没结果 (参数)->void;BiConsumer (参数1,参数2)->void
-
- /*统一数组处理机制*/
-
- // 我们给出一套Lambda方法处理数组的Demo函数来进行检测
-
- // 参数1,提供数组、可以是线程不安全数组或线程安全数组
- // 参数2,获取数组长度的方法
- // 参数3,自增方法,回传 array, index
- // 参数4,打印数组的方法
-
- private static <T> void demo(
- Supplier<T> arraySupplier,
- Function<T, Integer> lengthFun,
- BiConsumer<T, Integer> putConsumer,
- Consumer<T> printConsumer ) {
- List<Thread> ts = new ArrayList<>();
- T array = arraySupplier.get();
- int length = lengthFun.apply(array);
- for (int i = 0; i < length; i++) {
- // 每个线程对数组作 10000 次操作
- ts.add(new Thread(() -> {
- for (int j = 0; j < 10000; j++) {
- putConsumer.accept(array, j%length);
- }
- }));
- }
- ts.forEach(t -> t.start()); // 启动所有线程
- ts.forEach(t -> {
- try {
- t.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }); // 等所有线程结束
- printConsumer.accept(array);
- }
-
- /*不安全数组*/
-
- // 代码
- demo(
- ()->new int[10],
- (array)->array.length,
- (array, index) -> array[index]++,
- array-> System.out.println(Arrays.toString(array))
- );
-
- // 结果
- [9870, 9862, 9774, 9697, 9683, 9678, 9679, 9668, 9680, 9698]
-
- /*安全数组:大部分方法和原子整数以及原子引用完全相同*/
-
- // 代码
- demo(
- ()-> new AtomicIntegerArray(10),
- (array) -> array.length(),
- (array, index) -> array.getAndIncrement(index),
- array -> System.out.println(array)
- );
-
- // 结果
- [10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000]
我们首先来介绍一下字段更新器:
我们的字段更新器通常分为三种:
我们来进行代码展示:
- /*代码展示*/
-
- public class Test5 {
-
- // 在该类中设置字段
- private volatile int field;
-
- public static void main(String[] args) {
-
- // 创建字段更新器,前面为类名.class,后面为属性名
- AtomicIntegerFieldUpdater fieldUpdater =
- AtomicIntegerFieldUpdater.newUpdater(Test5.class, "field");
-
- // 我们需创建一个对象,作为字段更新器的类对象
- Test5 test5 = new Test5();
-
- // 修改0->10
- fieldUpdater.compareAndSet(test5, 0, 10);
- System.out.println(test5.field);
-
- // 修改10->20
- fieldUpdater.compareAndSet(test5, 10, 20);
- System.out.println(test5.field);
-
- // 修改10->30,修改失败!
- fieldUpdater.compareAndSet(test5, 10, 30);
- System.out.println(test5.field);
- }
- }
-
- /*结果展示*/
- 10
- 20
- 20
CAS专门创建了一种原子累加器,其由于性能远高于正常CAS,固被留下使用:
我们采用代码进行比对:
- /*主代码*/
-
- // 原子累加器
- for (int i = 0; i < 5; i++) {
- demo(() -> new LongAdder(), adder -> adder.increment());
- }
- // 正常CAS操作
- for (int i = 0; i < 5; i++) {
- demo(() -> new AtomicLong(), adder -> adder.getAndIncrement());
- }
-
- /*测试函数*/
-
- private static <T> void demo(Supplier<T> adderSupplier, Consumer<T> action) {
- T adder = adderSupplier.get();
- long start = System.nanoTime();
- List<Thread> ts = new ArrayList<>();
- // 4 个线程,每人累加 50 万
- for (int i = 0; i < 40; i++) {
- ts.add(new Thread(() -> {
- for (int j = 0; j < 500000; j++) {
- action.accept(adder);
- }
- }));
- }
- ts.forEach(t -> t.start());
- ts.forEach(t -> {
- try {
- t.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- });
- long end = System.nanoTime();
- System.out.println(adder + " cost:" + (end - start)/1000_000);
- }
-
- /*结果对比*/
-
- 1000000 cost:43
- 1000000 cost:9
- 1000000 cost:7
- 1000000 cost:7
- 1000000 cost:7
- 1000000 cost:31
- 1000000 cost:27
- 1000000 cost:28
- 1000000 cost:24
- 1000000 cost:22
这一小节我们将讲解无锁操作中的一些原理内容
首先我们给出LongAdder组成部分:
- /*LongAdder组成*/
-
- // 累加单元数组, 懒惰初始化
- transient volatile Cell[] cells;
-
- // 基础值, 如果没有竞争, 则用 cas 累加这个域
- transient volatile long base;
-
- // 在 cells 创建或扩容时, 置为 1, 表示加锁
- transient volatile int cellsBusy;
-
- /*Cell组成*/
-
- // 防止缓存行伪共享
- @sun.misc.Contended
- static final class Cell {
-
- volatile long value;
-
- Cell(long x) { value = x; }
-
- // 最重要的方法, 用来 cas 方式进行累加, prev 表示旧值, next 表示新值
- final boolean cas(long prev, long next) {
- return UNSAFE.compareAndSwapLong(this, valueOffset, prev, next);
- }
- // 省略不重要代码
- }
我们可以看到这里的是否创建cell采用的是一种CAS锁的机制,我们这里简单介绍一下:
- package cn.itcast.test;
-
- import lombok.extern.slf4j.Slf4j;
-
- import java.util.concurrent.atomic.AtomicInteger;
-
- import static cn.itcast.n2.util.Sleeper.sleep;
-
- @Slf4j(topic = "c.Test42")
- public class LockCas {
- // 0 没加锁
- // 1 加锁
- private AtomicInteger state = new AtomicInteger(0);
-
- public void lock() {
- while (true) {
- if (state.compareAndSet(0, 1)) {
- break;
- }
- }
- }
-
- public void unlock() {
- log.debug("unlock...");
- state.set(0);
- }
-
- public static void main(String[] args) {
- LockCas lock = new LockCas();
- new Thread(() -> {
- log.debug("begin...");
- lock.lock();
- try {
- log.debug("lock...");
- sleep(1);
- } finally {
- lock.unlock();
- }
- }).start();
-
- new Thread(() -> {
- log.debug("begin...");
- lock.lock();
- try {
- log.debug("lock...");
- } finally {
- lock.unlock();
- }
- }).start();
- }
- }
我们在Cell的类代码中可以看到这个注解:
首先我们知道内存之上还有缓存,其速度是具有极大差距的:
| 从 cpu 到 | 大约需要的时钟周期 |
|---|---|
| 寄存器 | 1 cycle (4GHz 的 CPU 约为0.25ns) |
| L1 | 3~4 cycle |
| L2 | 10~20 cycle |
| L3 | 40~45 cycle |
| 内存 | 120~240 cycle |
缓存的加入会造成数据副本的产生,即同一份数据会缓存在不同核心的缓存行中
同时CPU 要保证数据的一致性,如果某个 CPU 核心更改了数据,其它 CPU 核心对应的整个缓存行必须失效
我们给出简单示例图:

所以如果我们想要实现CAS的多处理器直接操作最后相加的想法就需要使缓存的这个特性变化:

因而我们就采用注解的方法:
这一小节我们将讲解Unsafe类
我们首先来简单介绍一下Unsafe:
Unsafe.getUnsafe()获得的unsafe不能用。我们给出简单例子展示:
- /*获得Unsafe*/
-
- public class UnsafeAccessor {
- static Unsafe unsafe;
- static {
- try {
- Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
- theUnsafe.setAccessible(true);
- unsafe = (Unsafe) theUnsafe.get(null);
- } catch (NoSuchFieldException | IllegalAccessException e) {
- throw new Error(e);
- }
- }
- static Unsafe getUnsafe() {
- return unsafe;
- }
- }
-
- /*unsafe使用(会操作底层内存等数据,尽量不要使用)*/
-
- //以下三个方法只执行一次,成功返回true,不成功返回false
- public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
-
- public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
-
- public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
-
- //以下方法都是在以上三个方法的基础上进行封装,会循环直到成功为止。
- 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 final long getAndAddLong(Object var1, long var2, long var4) {
- long var6;
- do {
- var6 = this.getLongVolatile(var1, var2);
- } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
-
- return var6;
- }
-
- public final int getAndSetInt(Object var1, long var2, int var4) {
- int var5;
- do {
- var5 = this.getIntVolatile(var1, var2);
- } while(!this.compareAndSwapInt(var1, var2, var5, var4));
-
- return var5;
- }
-
- public final long getAndSetLong(Object var1, long var2, long var4) {
- long var6;
- do {
- var6 = this.getLongVolatile(var1, var2);
- } while(!this.compareAndSwapLong(var1, var2, var6, var4));
-
- return var6;
- }
-
- public final Object getAndSetObject(Object var1, long var2, Object var4) {
- Object var5;
- do {
- var5 = this.getObjectVolatile(var1, var2);
- } while(!this.compareAndSwapObject(var1, var2, var5, var4));
下面我们讲解Unsafe来进行CAS操作的具体代码:
- /*unsafe实现字段更新*/
-
- // 主函数
- public class unsafeOperator{
-
- Unsafe unsafe = UnsafeAccessor.getUnsafe();
- Field id = Student.class.getDeclaredField("id");
- Field name = Student.class.getDeclaredField("name");
-
- // 获得成员变量的偏移量
- long idOffset = UnsafeAccessor.unsafe.objectFieldOffset(id);
- long nameOffset = UnsafeAccessor.unsafe.objectFieldOffset(name);
- Student student = new Student();
-
- // 使用 cas 方法替换成员变量的值
- UnsafeAccessor.unsafe.compareAndSwapInt(student, idOffset, 0, 20); // 返回 true
- UnsafeAccessor.unsafe.compareAndSwapObject(student, nameOffset, null, "张三"); // 返回 true
- System.out.println(student);
- }
-
- // 学生类
- @Data
- class Student {
- volatile int id;
- volatile String name;
- }
-
- // 输出
- Student(id=20, name=张三)
-
- /*unsafe实现原子整数*/
-
- // 主函数
- class AtomicData {
- private volatile int data;
- static final Unsafe unsafe;
- static final long DATA_OFFSET;
- static {
- unsafe = UnsafeAccessor.getUnsafe();
- try {
- // data 属性在 DataContainer 对象中的偏移量,用于 Unsafe 直接访问该属性
- DATA_OFFSET = unsafe.objectFieldOffset(AtomicData.class.getDeclaredField("data"));
- } catch (NoSuchFieldException e) {
- throw new Error(e);
- }
- }
- public AtomicData(int data) {
- this.data = data;
- }
- public void decrease(int amount) {
- int oldValue;
- while(true) {
- // 获取共享变量旧值,可以在这一行加入断点,修改 data 调试来加深理解
- oldValue = data;
- // cas 尝试修改 data 为 旧值 + amount,如果期间旧值被别的线程改了,返回 false
- if (unsafe.compareAndSwapInt(this, DATA_OFFSET, oldValue, oldValue - amount)) {
- return;
- }
- }
- }
- public int getData() {
- return data;
- }
- }
-
- // Account
- Account.demo(new Account() {
- AtomicData atomicData = new AtomicData(10000);
- @Override
- public Integer getBalance() {
- return atomicData.getData();
- }
- @Override
- public void withdraw(Integer amount) {
- atomicData.decrease(amount);
- }
- });
-
- /*手动实现原子整数完整版+测试*/
-
- public class UnsafeAtomicTest{
- public static void main(String[] args) {
- //赋初始值10000,调用demo后正确的输出结果为0
- AccountImpl account = new AccountImpl(10000);
- //结果正确地输出0
- account.demo();
- }
- }
-
- interface Account{
- //获取balance的方法
- int getBalance();
- //取款的方法
- void decrease(int amount);
- //演示多线程取款,检查安全性。
- default void demo(){
- ArrayList<Thread> ts = new ArrayList<>(1000);
- for (int i = 0; i < 1000; i++) {
- ts.add(new Thread(() -> {
- decrease(10);
- }));
- }
- for (Thread t:ts) {
- t.start();
- }
- for (Thread t:ts) {
- try {
- t.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- System.out.println(getBalance());
- }
- }
- //实现账户类,使用手动实现的原子整数作为余额类型
- class AccountImpl implements Account{
-
- UnsafeAtomicInteger balance;
-
- public AccountImpl(int balance){
- this.balance = new UnsafeAtomicInteger(balance);
- }
-
- @Override
- public int getBalance() {
- return balance.get();
- }
-
- @Override
- public void decrease(int amount) {
- balance.getAndAccumulate(amount,(x,y) -> y - x);
- }
-
- }
- //手动实现原子整数类
- class UnsafeAtomicInteger {
- //将value声明为volatile,因为乐观锁需要可见性。
- private volatile int value;
- //需要Unsafe的cas本地方法实现操作。
- private static final Unsafe unsafe;
- //偏移量,这两个变量很重要且通用、不可变,所以均声明为private static final
- private static final long offset;
-
- static{
- //静态代码块初始化unsafe
- unsafe = UnsafeAccessor.getUnsafe();
-
- try {
- //获取value在当前类中的偏移量
- offset = unsafe.objectFieldOffset(UnsafeAtomicInteger.class.getDeclaredField("value"));
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- //待研究
- throw new Error(e);
- }
- }
-
- public UnsafeAtomicInteger(){
-
- }
-
- public UnsafeAtomicInteger(int value){
- this.value = value;
- }
-
- public final int get(){
- return value;
- }
-
- public final boolean compareAndSet(int expext,int update){
- return unsafe.compareAndSwapInt(this, offset, expext, update);
- }
-
- public final int getAndIncrement(){
- //局部变量是必须的,因为多次从主存中读取value的值不可靠。
- int oldValue;
- while (true){
- oldValue = value;
- if(unsafe.compareAndSwapInt(this,offset,oldValue,oldValue + 1)){
- return oldValue;
- }
- }
- }
-
- public final int incrementAndGet(){
- int oldValue;
- while (true){
- oldValue = value;
- if (unsafe.compareAndSwapInt(this, offset, oldValue, oldValue + 1)) {
- return oldValue + 1;
- }
- }
- }
-
- public final int getAndDecrement(){
- int oldValue;
- while (true){
- oldValue = value;
- if (unsafe.compareAndSwapInt(this, offset, oldValue, oldValue - 1)) {
- return oldValue;
- }
- }
- }
-
- public final int decrementAndGet(){
- int oldValue;
- while (true){
- oldValue = value;
- if (unsafe.compareAndSwapInt(this, offset, oldValue, oldValue - 1)) {
- return oldValue - 1;
- }
- }
- }
-
- public final int getAndUpdate(IntUnaryOperator operator){
- int oldValue;
- int newValue;
- while (true){
- oldValue = value;
- newValue = operator.applyAsInt(oldValue);
- if (unsafe.compareAndSwapInt(this, offset, oldValue, newValue)) {
- return oldValue;
- }
- }
- }
-
- public final int updateAndGet(IntUnaryOperator operator){
- int oldValue;
- int newValue;
- while (true){
- oldValue = value;
- newValue = operator.applyAsInt(oldValue);
- if (unsafe.compareAndSwapInt(this, offset, oldValue, newValue)) {
- return newValue;
- }
- }
- }
-
- public final int getAndAccumulate(int x, IntBinaryOperator operator){
- int oldValue;
- int newValue;
- while (true){
- oldValue = value;
- newValue = operator.applyAsInt(x,oldValue);
- if (unsafe.compareAndSwapInt(this, offset, oldValue, newValue)) {
- return newValue;
- }
- }
- }
-
- public final int accumulateAndGet(int x, IntBinaryOperator operator){
- int oldValue;
- int newValue;
- while (true){
- oldValue = value;
- newValue = operator.applyAsInt(x,oldValue);
- if (unsafe.compareAndSwapInt(this, offset, oldValue, newValue)) {
- return oldValue;
- }
- }
- }
- }
-
- class UnsafeAccessor{
- public static Unsafe getUnsafe(){
- Field field;
- Unsafe unsafe = null;
- try {
- field = Unsafe.class.getDeclaredField("theUnsafe");
- field.setAccessible(true);
- unsafe = (Unsafe)field.get(null);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return unsafe;
- }
- }
我们在这里做一下该篇文章核心内容总结: