Synchronized 和CAS机制
- 从思想上来说,Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守。CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新。
- Synchronized关键字会让没有得到锁资源的线程进入BLOCKED状态,而后在争夺到锁资源后恢复为RUNNABLE状态,这个过程中涉及到操作系统用户模式和内核模式的转换,代价比较高。
- CAS机制的运用:一系列以Atomic开头的包装类。例如AtomicBoolean,AtomicInteger,AtomicLong
- 实现:CAS是英文单词Compare And Swap(Set)的缩写,翻译过来就是比较并替换
CAS步骤: 比如AtomicInteger对一个值(i=10)+1时,分为三步操作,1.取出原始值10 并存下;2.+1操作,预期值变为11;3.提交值11前,比较步骤1中的原始值和当前的i值是否一致,一致时,才提交预期值11到i中,否则提交失败,重新从步骤1开始,是一个无限循环过程,直到设置成功结束,这个过程又叫自旋。
CAS比较: 其实这个很像我们经常做的数据库的原子性操作,更新操作时,往往会对version(版本号)进行判断,来确认更新前没有其他用户更改过该行。但是它有没有版本号那么安全,会引发ABA问题。 - CAS中最主要的是compareAndSet方法
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
这里涉及到两个重要的对象,一个是unsafe,一个是valueOffset。unsafe为我们提供了硬件级别的原子操作,硬件级别的操作又称为原语操作,是顺序执行的。valueOffset为value变量的内存地址。
-
CAS缺点:
- CPU开销较大 在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。
- 不能保证代码块的原子性 CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。
- ABA问题 比如:线程1和2 都是加1操作。而线程3是减1操作。线程1 执行+1提交前。线程2和3 抢先执行了。这时当前的值依然是线程1操作时的拿到的原始值,依照CAS的原则,线程1认为值没有变,提交成功。但是实际上这个操作已经是非原子性的了。
针对ABA问题 AtomicStampedReference类就实现了用版本号做比较的CAS机制
网友评论