为什么无锁效率高
- 无锁情况下,即使重试失败,线程始终在高速运行,没有停歇,而 synchronized 会让线程在没有获得锁的时候,发生上下文切换,进入阻塞。打个比喻线程就好像高速跑道上的赛车,高速运行时,速度超快,一旦发生上下文切换,就好比赛车要减速、熄火,等被唤醒又得重新打火、启动、加速… 恢复到高速运行,代价比较大
- 但无锁情况下,因为线程要保持运行,需要额外 CPU 的支持,CPU 在这里就好比高速跑道,没有额外的跑道,线程想高速运行也无从谈起,虽然不会进入阻塞,但由于没有分到时间片,仍然会进入可运行状态,还是会导致上下文切换
CAS 的特点
- CAS 是基于乐观锁的思想:最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系,我吃亏点再重试呗
- synchronized 是基于悲观锁的思想:最悲观的估计,得防着其它线程来修改共享变量,我上了锁你们都别想改,我改完了解开锁,你们才有机会
- CAS 体现的是无锁并发、无阻塞并发
因为没有使用 synchronized,所以线程不会陷入阻塞,这是效率提升的因素之一
但如果竞争激烈,可以想到重试必然频繁发生,反而效率会受影响
适用于线程数少于核的cpu,因为在无锁情况下,需要额外的CPU保持线程的高速运行
为什么需要结合volatile使用?
- CAS 比较时,需要比较的是主内存的值。所以线程要保持对共享变量的可见性
实现原理
balance.addAndGet(-1 * amount);
等价于
while (true) {
int prev = balance.get();
int next = prev - amount;
if (balance.compareAndSet(prev, next)) {
break;
}
}
1:先获取原始值
2:获取需要更新之后的值
3:判断原始值是否被变更,如果已经改变,则重新进入循环
4:如果未改变,则执行变更
不可变设计
- 设置全局变量时,设置为 final,构造方法初始化,之后不再进行变更
- 保护性拷贝,不使用对象传递,创建新的对象,新的引用