我们先知道一个常识,在多个线程同时操作变量进行++的操作的时候,如果不加锁,肯定会导致数据错误的情况
这种情况下,我们就可以通过加锁来实现并发不出错误,将要修改的方法加synchronized锁,这样同一时间只能有一个线程获得变量,这样也会导致问题
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度掩饰,引起性能问题
- 一个线程持有锁会导致其他所有需要此锁的线程进入阻塞状态(挂起)
- 如果一个优先级高的线程等待一个优先级低的线程会导致优先级倒置,引起性能风险
这个时候选择volatile是不错的机制,但是volatile不保证原子性,所以还是要回到锁机制上来
独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁用到的机制就是CAS,Compare and Swap。
什么是CAS
compare and swap,中文翻译成比较并交换
CAS主要包括三个参数
- 内存位置(V)
- 原预期值(A)
- 新值(B)
CAS工作流程
- 从地址V中读取值A
- 按照程序要求获得值B
- 用CAS将V的值从A改为B,如果这段时间V中的值还是A没有改变,就可以操作成功
我们可以拿原子类的自增举例
public static void main(String[] args) {
AtomicInteger a = new AtomicInteger();
a.getAndIncrement();
}
我们进入getAndIncrement()方法

可以看到这个方法就是调用了一个unsafe类的方法然后就返回了,那么这个unsafe类又是什么呢
我们点进getAndAddInt方法

public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset); //根据o和offset获取内存地址中的值
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
我们可以看到这里的本质就是一个自旋锁,不断的获得对应内存地址中的值,然后如果这个值在执行操作的时候还没有改变就对这个值加一
使用CAS的好处
因为CAS底层是native方法,调用C的特性来对内存进行直接操作,可以大大提高性能
使用CAS存在的问题
CAS虽然能很高效的解决原子操作,但是仍然存在问题
- ABA问题
因为CAS只是判断获取值和在操作时这个值之间的时间该没改变来进行操作,当在这个时间内如果有一个操作修改了这个内存变量的值,由A改为B再改为A,这时CAS会认为这个值从来没有变过,但是值其实已经发生了一次改变 - 循环时间长时开销大
因为底层是自旋锁,当操作迟迟无法完成的时候,会对CPU带来非常大的开销 - 只能保证一个共享变量的原子操作
当对多个共享变量进行原子操作时,循环CAS就无法保证操作的原子性
参考文章
【狂神说Java】JUC并发编程最新版通俗易懂
Java中CAS详解
什么是CAS机制?
7359

被折叠的 条评论
为什么被折叠?



