我们都知道用Volatile修饰只能保证内存可见性, 并不保证原子性,JUC提供Atomic类,内部其实也是用Volatile保证内存可见性,但是用CAS(Compare-And-Swap)算法来保证数据的原子性(CAS是线程安全的)。
CAS的典型使用模式:首先从V中读取值A,并根据A计算新值B,然后通过CAS以原子方式将V中的值由A变成B(只要在这期间没有任何线程将V的值修改成其他值)。
CAS : 是硬件对于并发操作共享数据的支持。
包含了三个操作数
内存值V : 主存中要更新的变量
预估值A :表示预期的值
更新值B : 表示新值
当且仅当V == A时,V = B,否则 V 不等于 A,意味着已经被其他线程更改,当前线程放弃此操作。
在JDK里面提供了Unsafe类,封装了一些可用的CAS操作,AtomicInteger里面compareAndSet这个方法是调用unsafe.compareAndSwapInt这个方法,这个方法有四个参数,其中第一个参数为需要改变的对象,第二个为偏移量(即之前求出来的valueOffset的值),第三个参数为期待的值,第四个为更新后的值。
如果value的值(内存的value)与expect这个值相等,那么则将value修改为update这个值,并返回一个true,如果调用该方法时,value的值与expect这个值不相等,那么不做任何操作,并范围一个false。
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
Volatile的问题
- ABA问题
线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题
ABA --> 解决方式,添加版本号,类似AtomicStampedReference(个数 被动过几次),AtomicMarkableReference( Boolean 有没有被动过) - CPU消耗
CPU的耗费,如果CAS compareAndSetInt一直不成功,则会一直循环(自旋)。 - 单个volatile变量
只能保证一个共享变量的原子操作,不过可用采用AmoticReference类型解决这个问题。
网友评论