Java多线程系列10(CAS)

来源:互联网 发布:粒子群算法缺点 编辑:程序博客网 时间:2024/06/05 04:40

1 CAS原理

CAS(Compare And Swap)翻译过来就是:比较和交换。
CAS操作涉及三个操作数: 内存值V,旧的预期值A,要修改的新值B。只有当旧的预期值A和内存值V相等的时,将内存值修改为B,否则一直尝试,直到条件满足。
CAS由硬件特性实现,这些指令允许执行读-修改-写操作。即便有其他线程在同时修改变量,也不用担心。因为如果其他线程改变变量,CAS会检测到改变,重新从内存中读取值,重新对操作进行计算。
CAS操作的时候,不会阻塞线程,因此不会涉及到线程的阻塞以及线程之前的各种切换。CAS基于硬件特性来实现,可以获得更高的性能。
CAS和synchronized同步锁使用的场景如下:
(1)资源竞争并不频繁时:CAS由于不需要线程切换,自旋操作几率大。而synchronized需要阻塞线程,切换线程,性能相对低。
(2)资源竞争频繁时:CAS自旋概率变大,此时CPU的占用率反而更多。
关于CPU的锁,有三种:
(1)处理器自动保证基本内存操作的原子性。
处理器保证从系统内存中读写一个字节是原子的。有些处理器能够保证单处理器对同一个缓存行进行16、32、64位的操作是原子的。处理器提供总线锁定和缓存锁定两个机制来保证复杂内存操作的原子性。
(2)总线锁定保证原子性。
如果多个处理器同时对变量进行读写操作(比如i++),那么共享变量就会被多个处理器同时操作。这样读写操作就不是原子的。操作完之后恭喜共享变量的值和期望的值就可能不一致。比如:i=1,如果两个线程分别进行i++操作,结果可能是2,也有可能是3. 为2的时候,原因在于,多个处理器同时从各自的缓存中读取变量i,分别进行+1操作,然后分别写入系统内存中。要保证这个操作是原子的,必须保证一个CPU或者线程访问共享变量时,另外一个CPU不能操作缓存的变量。
使用总线锁可以解决这个问题:总线锁就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占使用该共享内存。总线锁定的时候,不能进行其他内存地址的操作,所以开销比较大。
(3)使用缓存锁保证原子性。
缓存锁指:就是如果缓存在处理器缓存行中内存区域在LOCK操作期间被锁定,当它执行锁操作回写内存时,处理器不在总线上声言LOCK#信号,而是修改内部的内存地址,并允许它的缓存一致性机制来保证操作的原子性,因为缓存一致性机制会阻止同时修改被两个以上处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓存行的数据时会起缓存行无效
#2 CAS的问题
虽然CAS能够解决原子问题。但是还是没有那么完美:
(1)ABA问题
ABA问题可以通过使用版本号来解决。
(2)循环时间开销问题
如果JVM支持处理器提供的pause指令,效率会有一定能够的提升。
(3)只能保证一个共享变量的原子操作。
AtomicReference可以解决这个问题。把多个变量放在一个对象里来做CAS操作
参考文档:http://blog.csdn.net/hsuxu/article/details/9467651

原创粉丝点击