乐观锁和悲观锁
多线程的并发操作可能会引发并发安全问题。实现并发控制的主要手段可以分为两种:乐观并发控制
和悲观并发控制
,即乐观锁
和悲观锁
。
悲观锁:对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁具有强烈的独占和排他特性。
乐观锁:假设数据一般情况下不会发生冲突,只在提交操作时检查是否违反数据完整性。乐观锁机制采取了更加宽松的加锁机制。
CAS原理
- compare and swap底层对应一条汇编指令:lock cmpxchg。该指令完全依赖于硬件功能,实现了原子操作。
- 获取原值时要保证原值对本线程可见。原值的可见性是使用java中的关键字volatile来保证的。
CAS存在的问题
1. ABA问题
其他线程修改数次的最后值和原值相同。
解决方法:引入版本号控制
。为数据增加一个版本标识version。当读取数据时,将version值一同读取出来,数据没更新一次,对version值加1。提交更新时,当前版本信息与第一次取出来的version值进行比对,如果当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
2. 自旋次数过多
CAS操作在不成功时会重新读取内存值并自旋重试。当系统的并发量非常高时,导致CAS操作失败并不断自旋重试。此时使用CAS并不能提高效率,反而会因为自旋次数过多还不如直接加锁进行操作的效率高。
3. 只能保证一个变量的原子性
CAS操作可以保证一个变量的原子性,但无法同时对多个变量进行CAS操作。可以将多个变量放入一个对象中进行CAS操作,比如AtomaticReference类;也可以直接使用锁操作多个变量。