一、简介
CAS即Compare and Swap,它体现一种乐观锁思想,被称之为无锁并发
比如多个线程要对一个共享的整形变量执行+1操作
//需要不断尝试
while(true){
int 旧值 = 共享变量;//比如拿到了当前值0,共享变量从主内存读到工作内存
int 结果 = 旧值+1;//在旧值0的基础上增加1,结果是1
/*
这时候如果别的线程把共享变量改成了5,本线程的正确结果是1 就作废了,这时候
compareAndSwap 返回false,重新尝试,直到:
compareAndSwap 返回true,表示我本线程做修改的同时,别的线程没有干扰
*/
if(compareAndSwap(旧值,结果)){
//尝试将结果赋值给共享变量,与此同时把旧值与当前共享变量做比较,因为怕写入结果时有其它线程把共享变量
//值改啦,所以拿上一次读到的值与共享变量当前的值做比较,如果两个一致,那结果可以成功写入到共享变量一 致,反之,其它线程把共享变量改啦,那么这一次操作就失败
//成功,推出循环
}
}
获取共享变量时,为了保证该变量的可见性,可能不是最新的,需要使用volatile修饰,结合CAS和volatile可以实现无锁,适用于竞争不激烈,多核CPU的场景下(不断尝试机制,需要利用CPU时间,如果CPU就一个CAS不从谈起,一个线程,其它线程在修改时,你想尝试也无CPU时间可用)
- 因为没有使用synchronized,所以线程不会陷入阻塞,这是效率提升的因素之一
- 但如果竞争激烈,可以想到重试必然频繁发生,反而效率会受影响
CAS底层依赖于一个Unsafe来直接调用操作系统底层的CAS指令
二、乐观锁与悲观锁
CAS是基于乐观锁的思想:最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系,我吃亏点在重试呗
synchronized是基于悲观锁的思想:最悲观的估计,得防着其它线程来修改共享变量,我上了锁你们都别想改,我改完了解开锁,你们才有机会
juc (java.util.concurrent) 中提供了原子操作类,可以提供线程安全的操作,例如AtomicInteger,AtomicBoolean 等,他们底层就是采用CAS技术+volatile来实现
public class JMMTest {
private static AtomicInteger atomicInteger = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Thread t1= new Thread(()->{
for(int i=0;i<5000;i++){
atomicInteger.getAndIncrement();//获取并且自增i++
}
});
Thread t2=new Thread(()->{
for(int i=0;i<5000;i++){
atomicInteger.getAndDecrement();//获取并且自减i--
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(atomicInteger);
}
}