一张图整体了解java concurrent包
image.png
CAS实现了区别于synchronouse同步锁的一种乐观锁。其与锁还是有着巨大的区别的,
1、性能方面
当多个线程访问临界区(数据共享的区域)的数据时,如果使用锁来进行并发控制,当某一个线程(T1)抢占到锁之后,那么其他线程再尝试去抢占锁时就会被挂起,当T1释放锁之后,下一个线程(T2)再抢占到锁后并且重新恢复到原来的状态大约需要经过8W个时钟周期。而假设我们业务代码本身并不具备很复杂的操作,执行整个操作可能就花费3-10个时钟周期左右,那么当我们使用无锁操作时,线程T1和线程T2对共享变量进行并发的CAS操作,假设T1成功了,T2最多再执行一次,它执行多次的所消耗的时间远远小于由于线程所挂起到恢复所消耗的时间,它基本不可能运气差到要执行几千次才能完成操作,因此无锁的CAS操作在性能上要比同步锁高很多。
2、实际业务
使用同步锁机制锁保证的"先行发生原则(happen before)"过于的粗力度,它虽然可以保证线程T1的操作如果早于线程T2获取锁,那么T1一定在T2之前完成操作;而CAS操作却不能保证这样的顺序的一致性,但是CAS操作保证了关键的修改一步具有先行发生原则。在我们实际的业务场景下,由锁机制保证的这种看似所谓的有序性其实没有太大的意义,因为我们只需保证最终结果的一致性就能满足业务的需要。我们以商品秒杀为例,当多个用户并发访问时,我们其实只需确保的就是其在抢占的那一刻是一个原子操作即可,当商品数目为0时提示操作失败,而无需保证先来的用户一定能够抢到商品。因此,在业务本身的需求上,无锁机制本身就可以满足我们绝大多数的需求,并且在性能上也可以大大的进行提升。
CAS问题
所有的事物都有两面性,CAS虽然很高效的解决了原子操作,但是其本身还是存在缺陷的。
1、循环时间太长
在Java代码示例中我们可以看到用的是无限循环。如果一直不成功怎么办,在自旋CAS长时间不成功,为给CPU带来很大的开销。
2、保证单个共享变量是原子操作
通过代码示例也可以看出,CAS只能针对单个变量;如果是多个变量那么就要使用锁了。 从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作(后续会把该部分内容补充上)。
3、ABA问题
从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题
网友评论