volatile
volatile是Java虚拟机提供的轻量级的同步机制,当多个线程进行操作共享数据时,可以保证内存中的数据可见。
JMM有3个特性
1:可见性
2:原子性
3:有序性(禁止指令重排)
其中volatile满足可见性和有序性,但是不满足原子性
注意:
1:volatile 不具备“互斥性”(synchronized有互斥性)
2:volatile 不能保证变量的“原子性”(多线程访问自增变量,多线程各自读取到以后再写入,可能会重复写,volatile只能保证在主存中操作,但是不能保证原子性)。
原子性的解决方法
1:synchronized
2:CAS算法+volatile。
CAS算法
CAS(Compare-And-Swap)是一种硬件对并发的支持,是一条CPU并发原语。它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。
CAS 包含了三个操作数:
①内存值 V ②预估值 A ③更新值 B
当且仅当 V == A 时, V = B; 否则,不会执行任何操作。
原理:do…while实现的自旋锁
java.util.concurrent.atomic.AtomicInteger->getAndIncrement()
内部调用:sun.misc.Unsafe->unsafe.getAndAddInt(this, valueOffset, 1)
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
CAS缺点:
1:循环时间长开销大
2:只能保证一个共享变量的原子操作
3:引发ABA问题
ABA问题
调用java.util.concurrent.atomic.AtomicReference的compareAndSet会有ABA问题。
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
如何解决:
用带有时间戳的原子引用
java.util.concurrent.atomic.AtomicStampedReference
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}