|
| 1 | +> CAS 知道吗,如何实现? |
| 2 | +> |
| 3 | +> 讲一讲AtomicInteger,为什么要用CAS而不是synchronized? |
| 4 | +> |
| 5 | +> CAS底层原理,谈谈你对UnSafe 的理解? |
| 6 | +> |
| 7 | +> AtomicInteger 的ABA问题,能说一下吗,原子更新引用知道吗? |
| 8 | +> |
| 9 | +> 如何规避 ABA 问题? |
| 10 | +
|
| 11 | +从 Atomic 原子类,到 CAS |
| 12 | + |
| 13 | +i++ 在多线程环境下用 AtomicInteger 的 getAndIncrement() 为什么是线程安全的 |
| 14 | + |
| 15 | +```java |
| 16 | +public class CASDemo { |
| 17 | + |
| 18 | + public static void main(String[] args) { |
| 19 | + |
| 20 | + AtomicInteger num = new AtomicInteger(6); |
| 21 | + System.out.println(num.compareAndSet(6, 7) + "\t + current num:" + num); |
| 22 | + System.out.println(num.compareAndSet(6, 8) + "\t current num:" + num); |
| 23 | + } |
| 24 | +} |
| 25 | +``` |
| 26 | + |
| 27 | + |
| 28 | + |
| 29 | +#### 1. UnSafe |
| 30 | + |
| 31 | +是CAS的核心类,由于 Java 方法无法直接访问底层系统,需要通过本地(native)方法来访问,UnSafe 相当于一个后门,基于该类可以直接操作特定内存的数据。UnSafe 类存在与 sum.misc 包中,其内部方法可以像 C 的指针一样直接操作内存,因为Java 中CAS 操作的执行依赖于 UnSafe 类的方法。 |
| 32 | + |
| 33 | +UnSafe 类中的所有方法都是 native 修饰的,也就是说该类中的方法都是直接调用操作系统底层资源执行相应任务。 |
| 34 | + |
| 35 | +#### 2. valueOffset |
| 36 | + |
| 37 | +变量 valueOffset 表示该变量值在内存中的偏移地址,因为UnSafe 就是根据内存偏移地址获取数据 |
| 38 | + |
| 39 | +```java |
| 40 | +public final int getAndIncrement() { |
| 41 | + return unsafe.getAndAddInt(this, valueOffset, 1); |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +#### 3.变量 value用volatile 修饰,保证了多线程之间的内存可见性 |
| 46 | + |
| 47 | + |
| 48 | + |
| 49 | +## CAS 是什么 |
| 50 | + |
| 51 | +CAS: 全称Compare and swap,它是一条 **CPU 并发原语**。 |
| 52 | + |
| 53 | +他的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。 |
| 54 | + |
| 55 | +CAS 并发原语体现在 Java 语言中的 sum.misc.Unsafe 类中的各个方法。调用Unsafe 类中的CAS 方法, JVM 会帮助我们实现出 CAS 汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,由于CAS是一种系统原语,原语属于操作系统用于范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,CAS是一条CPU的原子指令,不会造成数据不一致问题。 |
| 56 | + |
| 57 | +逐层看getAndIncrement() 的源码如下 |
| 58 | + |
| 59 | +```java |
| 60 | +public final int getAndAddInt(Object var1, long var2, int var4) { |
| 61 | + int var5; |
| 62 | + do { |
| 63 | + var5 = this.getIntVolatile(var1, var2); |
| 64 | + } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); |
| 65 | + |
| 66 | + return var5; |
| 67 | +} |
| 68 | +``` |
| 69 | + |
| 70 | +val1 AtomicInteger对象本身 |
| 71 | + |
| 72 | +var2 该对象值得引用地址 |
| 73 | + |
| 74 | +var4 需要变动的数量 |
| 75 | + |
| 76 | +var5 是用过var1 var2 找出的主内存中真实的值(通过内存偏移量) |
| 77 | + |
| 78 | +用该对象当前的值与var5 比较,如果相同,更新var5 + var4 并且返回true |
| 79 | + |
| 80 | +如果不同,继续取值然后再比较,直到更新完成 |
| 81 | + |
| 82 | +没有加锁,反复执行,既保证了一致性,又保证了并发性 |
| 83 | + |
| 84 | + |
| 85 | + |
| 86 | + |
| 87 | + |
| 88 | + |
| 89 | + |
| 90 | + |
| 91 | + |
| 92 | + |
| 93 | + |
| 94 | + |
| 95 | + |
| 96 | + |
| 97 | + |
| 98 | +## CAS 缺点 |
| 99 | + |
| 100 | +- 循环时间长,开销很大 |
| 101 | + |
| 102 | + do while 如果CAS失败,会一直进行尝试,如果CAS长时间一直不成功,可能会给CPU带来很大的开销 |
| 103 | + |
| 104 | +- 只能保证一个共享变量的原子操作 |
| 105 | + |
| 106 | + 当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是,对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性。 |
| 107 | + |
| 108 | +### ABA 问题 |
| 109 | + |
| 110 | +aba问题如何产生的 |
| 111 | + |
| 112 | + |
| 113 | + |
| 114 | +### 原子引用 |
| 115 | + |
| 116 | +AtomicReference |
| 117 | + |
| 118 | + |
| 119 | + |
| 120 | +解决ABA问题, 新增一种机制,修改版本号(类似时间戳) |
| 121 | + |
1 | 122 | ### 2. 原子变量 CAS算法 |
2 | 123 |
|
3 | 124 | #### CAS 算法 |
|
6 | 127 | - CAS 是一种无锁的非阻塞算法的实现。 |
7 | 128 | - CAS 包含了 3 个操作数: |
8 | 129 | - 需要读写的内存值 V |
9 | | - - 进行比较的值 A |
10 | | - - 拟写入的新值 B |
| 130 | + - 旧的预期值 A |
| 131 | + - 要修改的更新值 B |
11 | 132 | - 当且仅当 V 的值等于 A 时,CAS 通过原子方式用新值 B 来更新 V 的 值,否则不会执行任何操作。 |
12 | 133 |
|
13 | 134 |
|
|
27 | 148 |
|
28 | 149 |
|
29 | 150 |
|
| 151 | +## 原理 |
| 152 | + |
| 153 | +自旋锁 UnSafe类 |
| 154 | + |
30 | 155 | ## Java的Atomic类分析 |
31 | 156 |
|
32 | 157 | https://blog.csdn.net/goodlixueyong/article/details/51339689 |
0 commit comments