学过多线程就会接触到并发,并发再多线程中的重要性不言而喻,在Java中还有并发包,里面实现了各种各样的方法来帮助我们解决多线程带来的各种问题。而要想读懂这些底层问题,CAS是绕不过的知识,大多底层都是以CAS来实现的。今天就带大家来学习CAS相关的知识。
CAS: 全称Compare and swap,字面意思:" 比较并交换 "
一个CAS涉及到的操作数有:
有了这三个操作数,在看看它的操作:
这也就侧面的表现出了对于多个线程都对某个值进行修改时,保证了修改前拿到的值是期望值才会操作。
还记得之前多线程的经典例子嘛,就是多个线程同时对一个共享变量进行修改值,最终修改后的值大概率不是正确的结果。也就是对于i ++ 操作,在多线程中是保证不了它的正确性。
原因呢,就是i ++ 本身并不是一个原子性的操作,它可以分成三步:
这就导致多线程在执行该操作时,线程A、B可能同时从主内存中获得一个值后分别 +1 后写回到主内存。导致结果的错误,这时候你就会想到 那用 锁 呗! 所以解决的方式就是使用synchronized关键字来进行加锁。
的确,这种多线程导致的原子性问题可以加锁,使得众多线程竞争锁,拿到锁的线程才可以进行下一步的操作,其它线程则都开始阻塞,直到这个线程释放了锁后,唤醒其它线程,再次开始竞争锁。
但对于线程的阻塞和唤醒都是非常消耗时间的! 如果像i ++ 的操作,仅仅只是每次进行加一操作就要经历线程唤醒和重新竞争锁,未免有些大材小用。
而这时候,就可以使用CAS机制中的compareAndSet
方法,也就是比较并设值。
当多个线程同时对某个资源进行CAS操作,只能有一个线程操作成功,但是并不会阻塞其他线程,其他线程只会收到操作失败的信号。可见 CAS 其实是一个乐观锁。
看了上面的解释,你可能还会有些疑惑,CAS怎么保证的在设置值的时候的原子性呢?看起来也是进行了比较值和设置值的操作呀?
其实,这都是操作系统的功劳!在操作系统中这么多操作实际上就是一条指令操作。
针对不同的操作系统,JVM 用到了不同的 CAS 实现原理:
看起来很复杂,其实只要知道:是因为硬件予以了支持,软件层面才能做到。
实现原子类
Java中的atomic包下的原子类,都是通过CAS实现的。
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
可以参考另一篇文章——> Java中atomic包下的原子操作类
AtomicStampedReference
这个类是可以提供版本控制的。由以上着问题我们也可以看出来,CAS由于其不会阻塞线程的特点,而是一直在循环,这就使得它的适用场景就是代码能很快的执行,如果代码执行时间过长,就会导致其它线程调用方法长时间处于失败状态。
好啦,这就是CAS的一些基本的知识了,自己总结的也还有些不到位,如果大家感兴趣,建议研究源码,可以学到很多。如果文章有什么问题,欢迎留言指正。也欢迎点赞关注一起进步