CAS是什么
cas:比较并交换(compareAndSet)
想突破一万五,必须要说得出底层!互联网公司不是不招,而是招高手。
多线程,发生的原子性问题,是因为执行太快发生了覆盖,值丢失了。
看方法的源码:
两个参数,一个是期望值,一个是更新值,主内存空间中有个变量a,3个线程把a复制到了工作内存中,然后想要修改主内存中的a,肯定希望此时主内存中还是自己复制过来的a,这就是第一个参数,期望值,如果不是a了,就返回false(整个流程就得重新取得主内存中的a的值,然后重复这个操作,直到期望值跟主内存中的值匹配起来。),如果是a,就返回true,并设置值。
测试代码:
这就说明,只有期望值和实际值一样,才会执行赋值操作,返回true,如果实际值不满足期望值,就会返回false,不会赋值。这就是最明显的比较并交换。
总结:如果线程的期望值,跟物理内存的真实值一样,就修改为更新的值,本次操作为true,反之,为false。
CAS底层原理-上
底层原理,两个关键字:自旋锁、unsafe类。
atomicInteger.getAndIncrement()为啥可以保证原子性
getAndIncrement()用一个方法解决了i++在多线程环境下的线程安全的问题。
底层代码实现:调用的是unsafe类。
this:当前对象,valueoffset:内存偏移量,说白了就是内存地址。1是固定的。
这个方法的意思是:当前对象的内存地址值是多少?
这个类是运行时环境rt.jar提供支持的。
具体的位置在:
这个类大部分都是native级别的方法。
源码级别解读:unsafe类
value被volatile修饰,只要一变化,其他线程都可见。
静态代码块给调用unsafe类给内存偏移量赋值。
原子类之所以在多线程环境下不用加sync也能保证线程安全,是因为它用的是unsafe类。
可以用类似指针的方式,获取数据。获取的手段极度精确,如几排几列这种叫法。获取之后,执行加一的操作。
这个变量就是具体的值了。
cas是cpu的原子指令,不会造成数据的不一致问题。也就是线程安全的。
var5是获得var1(传入的this,也就是当前对象)的var2(内存的偏移地址),可有理解为指针var5指向this的内存地址。
这个操作结束之后,进行while判断,var1的var2位置==var5的值是不是一致,如果是期望的值,那么就改为var5+var4,实现了加一。返回true,然后被取反就跳出了循环。如果不一致,则说明被人改过了,则返回false,取反为true,就继续循环,再去主内存拿一次值,直到比较成功。
cas底层原理-下
sync加锁同一时间段只允许有一个线程来访问,一致性得到了保障,但是大大降低了效率,并发性下降。使用cas没有加锁,匹配不上会循环比较,直到比较成功,大大提高了性能。
总结:
cas缺点
cas不加锁,保证了一致性,但是它有3个问题。
- 循环时间长
- 只能保证一个共享变量的原子操作
多个变量只能加锁了。 - 他会引出来ABA问题