一、原子性高频问题:
1.1 Java中如何实现线程安全?
多线程操作共享数据出现的问题。
锁:
- 悲观锁:synchronized,lock
- 乐观锁:CAS
可以根据业务情况,选择ThreadLocal,让每个线程玩自己的数据。
1.2 CAS底层实现
最终回答:先从比较和交换的角度去聊清楚,在Java端聊到native方法,然后再聊到C++中的cmpxchg的指令,再聊到lock指令保证cmpxchg原子性
Java的角度,CAS在Java层面最多你就能看到native方法。
你会知道比较和交换:
- 先比较一下值是否与预期值一致,如果一致,交换,返回true
- 先比较一下值是否与预期值一致,如果不一致,不交换,返回false
可以去看Unsafe类中提供的CAS操作
四个参数:哪个对象,哪个属性的内存偏移量,oldValue,newValue
native是直接调用本地依赖库C++中的方法。
https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/prims/unsafe.cpp
https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/os_cpu/linux_x86/vm/atomic_linux_x86.inline.hpp
在CAS底层,如果是多核的操作系统,需要追加一个lock指令
单核不需要加,因为cmpxchg是一行指令,不能再被拆分了
看到cmpxchg ,是汇编的指令,CPU硬件底层就支持 比较和交换 (cmpxchg),cmpxchg并不保证原子性的。(cmpxchg的操作是不能再拆分的指令)
所以才会出现判断CPU是否是多核,如果是多核就追加lock指令。
lock指令你可以理解为是CPU层面的锁,一般锁的粒度就是 缓存行 级别的锁,当然也有 总线锁 ,但是成本太高,CPU会根据情况选择。
1.3 CAS的常见问题
ABA: ABA不一定是问题!因为一些只存在 ++,–的这种操作,即便出现ABA问题,也不影响结果!
线程A:期望将value从A1 - B2
线程B:期望将value从B2 - A3
线程C:期望将value从A1 - C4
按照原子性来说,无法保证线程安全。
解决方案很简单,Java端已经提供了。