一、原子性高频问题:
1.1 Java中如何实现线程安全?
多线程操作共享数据会出现问题。可以使用锁来解决:
- 悲观锁: 使用
synchronized
和Lock
- 乐观锁: 使用 CAS(Compare-And-Swap)
可以根据业务情况选择 ThreadLocal
,让每个线程处理自己的数据。
1.2 CAS底层实现
回答思路: 先解释比较和交换的概念,在Java层面讨论native方法,再深入到C++中的 cmpxchg
指令,最后谈到 lock
指令保证 cmpxchg
的原子性。
在Java中,CAS(Compare-And-Swap)在Java层面可以看到 Unsafe
类中的native方法。
基本操作步骤:
- 比较当前值与预期值是否一致,如果一致,交换并返回
true
。 - 如果不一致,不交换并返回
false
。
可以查看 Unsafe
类中提供的CAS操作,有四个参数:对象、属性的内存偏移量、旧值和新值。
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
CAS的底层实现涉及到 cmpxchg
指令。在多核系统中,需要 lock
指令来保证 cmpxchg
的原子性。
cmpxchg
是一个汇编指令,支持比较和交换操作。cmpxchg
本身不保证原子性,所以需要lock
指令。lock
指令在CPU层面相当于一个锁,通常粒度是缓存行级别,有时是总线锁,但成本较高。
1.3 CAS的常见问题
-
ABA问题: ABA问题不一定是问题。例如,仅有++,–的操作,不会影响结果。解决方案是使用带版本号的CAS,如
AtomicStampedReference
。
-
自旋次数过多: 自旋次数过多会消耗大量的CPU资源。解决方法:
- 从
synchronized
方向:CAS多次失败后,将线程挂起,避免占用过多CP
- 从