原子类型
原子类型在java.util.concurrent.atomic包下 有四种类型(每种各3个类):
基本类型
AtomicInteger:原子操作整数类型
AtomicBoolean:基本同上,操作布尔类型
AtomicLong:基本同上,操作Long类型
引用类型
AtomicReference:引用普通的对象,在并发场景下修改对象时保证线程安全
AtomicStampedReference:在AtomicReference基础上增加有版本号.
AtomicMarkableReference:在AtomicReference基础上增加有标记位.
数组类型
AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray
更新属性类型:
AtomicIntegerFieldUpdater
AtomicLongFieldUpdater
AtomicReferenceFieldUpdater
CAS
原理
CAS(V,E,N): V表示操作对象的值,E表示预期值,N表示新值
当要执行更新操作时,会先在同步方法中比较V和E是否相等,如果相等说明V没有被其他线程所修改,这时才会用N替换V,否则什么也不做.
sun.misc.Unsafe类:
该类提供了绕开JVM的功能,如实例化只有私有构造方法的类,通过内存偏移地址修改变量值,创建容量大于Integer.MAX_VALUE的数组等.是CAS实现的核心.
主要提供了一下3种CAS方法:
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x);
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);
AtomicInteger
i++和++i是非线程安全的,想要在多线程环境下使用,很自然的就想到了使用synchronized,
实际上AtomicInteger也是线程安全的,但和synchronized的理念不同,是基于CAS原理实现的.
结构
private static final Unsafe unsafe = Unsafe.getUnsafe();
// value内存地址
private static final long valueOffset;
// 存储值的变量
private volatile int value;
以incrementAndGet()方法为例:
Java7中:
public final int incrementAndGet() {
for (;;) {// CAS实现的关键
int current = get();// 获得当前值
int next = current + 1;// 增
if (compareAndSet(current, next))// 传入预期值和更新值
return next;
}
}
// 抽出compareAndSet()方法
public final boolean compareAndSet(int expect, int update) {
// 更新时,会将expect和现在内存中的值比较,相同才会用update覆盖内存中的值
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
Java8中:
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
// Unsafe类中的getAndAddInt(),delta:预期增加的量
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);// native方法
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
JDK中似乎没有Unsafe的源码,可参看OpenJDK-Unsafe
可以看出,无论是7还是8中,最终都是使用了Unsafe类的compareAndSwapInt()进行预期值和新值的比较.
AtomicReference
AtomicReference
AtomicReference<String> atomicReference = new AtomicReference<String>();
atomicReference.set("newValue");
atomicReference.compareAndSet("newValue", "updateUser");
// atomicReference.getAndSet("newValue", "updateUser");
结构上和AtomicInteger不同的就是存的对象不再是基本类型,常用的方法也都是类似的,但存在ABA问题.
ABA问题
ABA问题:线程1准备修改变量,获取到值为A,此时线程2将其修改成B,然后又修改回A, 对于线程1来说变量并没有发生变化,但实际上发生了变化,对于无状态的数据来说没有特殊的影响,但对于引用类型就不是这样了,尤其是针对链式结构.
AtomicStampedReference
private static class Pair<T> {
final T reference;// 存储用变量
final int stamp;// 版本号
}
private volatile Pair<V> pair;// 存储用变量(容器)
// 构造方法
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
只有一个构造方法,实例化时就要指定版本号,创建一个AtomicStampedReference对象,实质就是创建了一个Pair对象. 由于版本号的存在,一定程度上能够解决ABA问题.
其compareAndSet更新操作如下:
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))); // 对象和版本号有变更的更新
}
// 上面的casPair
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
值得注意的是,此处使用的是&&逻辑符,当其前面出现false的结果时,后面的内容就不再执行, 因此,预期值和内存值不一致,后面的casPair就不会执行,上面的return语句很好的符合CAS原理.
AtomicMarkableReference和AtomicStampedReference基本相同,只不过提供的不是版本号,而是一个标志位.
Atomic Array
AtomicIntegerArray
int[] value = new int[] { 1, 2, 3, 4, 5 };
AtomicIntegerArray a3 = new AtomicIntegerArray(value);
a3.getAndSet(2, 100);
内部的元素也都可以使用AtomicInteger的操作.
数组元素类型也可以是应用类型,此时就可以使用AtomicReferenceArray类.
Atomic FieldUpdater
都是抽象类,可以通过newUpdater()方法获取其对象, AtomicIntegerFieldUpdater针对的是引用对象的普通类型属性,AtomicReferenceFieldUpdater针对的是引用对象的引用类型属性.
使用例:
AtomicIntegerFieldUpdater a4 = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
User user = new User(0, null);
a4.getAndIncrement(user);
AtomicReferenceFieldUpdater a5 = AtomicReferenceFieldUpdater.newUpdater(User.class, User.class, "next");
User user1 = new User(2, user);
User user2 = new User(3, user1);
a5.getAndSet(user2, user);
static class User {
public volatile int age = 8;
public volatile User next;
User(int age, User next) {
this.age = age;
this.next = next;
};
}