Atomic原子类就是利用自旋+CAS来保证线程安全的。有以下几个基础类:
它还维护了一些保证原子性修改元素的集合,不过一般用不上:
甚至还可以保证对象的基础类型的原子性修改:
用AtomicInteger来举例子,本质就是用volatile来维护一个值。
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
private volatile int value;
public AtomicInteger(int initialValue) {
value = initialValue;
}
}
但是volatile只能保证这个变量的可见性,不能保证他的原子性,所以在操作的时候,用到了UnSafe类。
可以看看getAndIncrement这个类似i++的函数,可以发现,是调用了UnSafe中的getAndAddInt。
public final int getAndIncrement() {
return U.getAndAddInt(this, VALUE, 1);
}
再深入看,就发现是一个 自旋+CAS了,在这个过程中,通过compareAndSwapInt比较更新value值,如果更新失败,重新获取旧值,然后更新,这样就保证了原子性的增加了。
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!weakCompareAndSetInt(o, offset, v, v + delta));
return v;
}
public final boolean weakCompareAndSetInt(Object o, long offset,
int expected,
int x) {
return compareAndSetInt(o, offset, expected, x);
}
CAS相对于其他锁,不会进行内核态操作,有着一些性能的提升。但同时引入自旋,当锁竞争较大的时候,自旋次数会增多。cpu资源会消耗很高。
换句话说,CAS+自旋适合使用在低并发有同步数据的应用场景。
但是,有一点需要注意的是,大部分的原子类都是不能解决ABA的问题的,所以新加了一个AtomicStampedReference类,它会维护一个Pair,然后Pair里会携带类似版本的字段stemp,在CAS额外判断stemp的值来解决ABA问题:
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)));
}