前言在谈CAS算法时,我们先来了解一下无锁的概念 。
无锁分为以下两大派系:
- 对于乐观派系而言,它们认为事情总会往好的方向去发展,总是认为坏的情况发生概率特别小,可以无所顾忌的做任何事情;
- 对于悲观派系而言,它们总会认为发展事态如果不及时控制,以后就无法挽回,即时此种局面不会发生的情况下 。
什么是CAS?CAS全称为Compare And Swap即比较并交换,其算法公式如下:
函数公式:CAS(V,E,N)
V:表示要更新的变量
E:表示预期值
N:表示新值
文章插图
CAS原理图
如果V值等于E值,则将V的值设为N 。若V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做 。通俗的理解就是CAS操作需要我们提供一个期望值,当期望值与当前线程的变量值相同时,说明还没线程修改该值,当前线程可以进行修改,也就是执行CAS操作,但如果期望值与当前线程不符,则说明该值已被其他线程修改,此时不执行更新操作,但可以选择重新读取该变量再尝试再次修改该变量,也可以放弃操作 。原子包 JAVA.util.concurrent.atomic(锁自旋)JDK1.5 的原子包:java.util.concurrent.atomic 这个包里面提供了一组原子类 。其基本的特性就 是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个 线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由 JVM 从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解 。
相对于对于 synchronized 这种阻塞算法,CAS 是非阻塞算法的一种常见实现 。由于一般 CPU 切换时间比 CPU 指令集操作更加长,所以 J.U.C 在性能上有了很大的提升 。如下代码:
文章插图
getAndIncrement 采用了 CAS 操作,每次从内存中读取数据然后将此数据和+1 后的结果进行CAS 操作,如果成功就返回结果,否则重试直到成功为止 。而 compareAndSet 利用 JNI 来完成CPU 指令的操作 。
文章插图
ABA问题CAS 会导致“ABA 问题” 。CAS 算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化 。
比如说一个线程 one 从内存位置 V 中取出 A,这时候另一个线程 two 也从内存中取出 A,并且two 进行了一些操作变成了 B,然后 two 又将 V 位置的数据变成 A,这时候线程 one 进行 CAS 操作发现内存中仍然是 A,然后 one 操作成功 。尽管线程 one 的 CAS 操作成功,但是不代表这个过程就是没有问题的 。
部分乐观锁的实现是通过版本号(version)的方式来解决 ABA 问题,乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1 操作,否则就执行失败 。因为每次操作的版本号都会随之增加,所以不会出现 ABA 问题,因为版本号只会增加不会减少 。
【浅谈Java并发编程之CAS算法策略】
推荐阅读
- 作为Java程序员,你知道 new 和 newInstance 的区别吗?
- 8种创建Java线程的方式,你知道几个?
- Java架构-还不了解Flink底层RPC使用的框架和原理?那就认真看完
- 总结Java中return语句的用法
- 省林业厅我省制定并发布西藏茶园种植技术规范
- 一道简单面试题引出的Java数据类型连环问
- javascript实现web通讯的几种方式
- 食道癌易在春季复发 还要当心这些并发症
- Java 中 long 和 double 的原子性?
- 几种处理JavaScript异步操作的办法