引用了其他一些博文的片段
1.jdk8 中的原子类都是用无锁方式和volatile修饰变量实现的(乐观锁来实现,具体是cas,比较并交换,该指令有cpu保证原子性),原子类在java.util.concurrent.atomic包中,包括原子更新基本类型(AtomicBoolean,AtomicInteger,AtomicLong),原子更新数组,通过原子的方式更新数组里的某个元素(AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray),原子更新引用类型(AtomicReference,AtomicReferenceFieldUpdater,AtomicMarkableReferce),原子更新字段类(AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicStampedFieldUpdater,AtomicReferenceFieldUpdater)
2.java原子类使用的cas函数,是在unsafe类中,Unsafe类所有方法都是native的,直接调用操作系统底层资源执行相应任务,它可以像C一样操作内存指针,是非线程安全的。Java方法无法直接访问地层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存数据。其内部方法操作可以像C的指针一样直接操作内存。
3.操作系统如何保证指令的原子性呢,
1) 关中断,cpu一条执行不被中断
2)总线锁,当一个处理器想要更新某个变量的值时,向总线发出LOCK#信号,此时其他处理器的对该变量的操作请求将被阻塞,发出锁定信号的处理器将独占共享内存,于是更新就是原子性的了。
4.AtomicInteger
会获取value对象的地址,存入到valueOffset的变量中。
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
通过unsafe类的getAndAddInt来实现原子赠一
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
通过 unsafe类的compareAndSwapInt方法,实现对integer的比较和交换
/**
* 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);
}
cas失败的话,就自旋,不断重试,直到成功。
//Unsafe类中的getAndAddInt方法
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;
}
5.cas是什么?
CAS全称呼Compare-And-Swap,它是一条CPU并发原语
他的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。
CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中各个方法。调用Unsafe类中的CAS方法,JVM会帮我们实现CAS汇编指令。这是一种完全依赖于硬件的功能,通过他实现了原子操作。由于CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成数据不一致问题。
6. cas 的缺点
但是CAS机制通常也存在以下缺点:
(1)ABA问题
如果V的初始值是A,在准备赋值的时候检查到它仍然是A,那么能说它没有改变过吗?也许V经历了这样一个过程:它先变成了B,又变成了A,使用CAS检查时
以为它没变,其实却已经改变过了。
(2)CPU开销较大
在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。
(3)不能保证代码块的原子性
CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。
7.多个变量的修改,考虑AtomicReference,多个变量修改且避免ABA的问题,考虑用AtomicStampedReference类
从AtomicReference实现类来看,AtomicReference类只是对引用的本身值的赋值做了原子保护,引用指向的对象的内容的变化,并没有提供原子保护。只是用AtomicReference来设置对象时,是新建了一个对象来设置,比较用的excpect值是cas函数之前读取出来用做比较的。如果其他线程也新建对象并设置成功的话,当前线程cas就会失败,然后不断自旋,重新读取新的对象,改变后再设置,直到成功。
参考:https://www.jianshu.com/p/882d0e2c3ea6
public class AtomicReference<V> implements java.io.Serializable {
private static final long serialVersionUID = -1848883965231344442L;
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile V value;
AtomicStampedReference类通过引入stamp,将stamp和要更新的对象reference组合成pair, 如果stamp和reference都和前期取出来的一致,才更新为新的pair。
//关键代码
public class AtomicStampedReference<V> {
private static class Pair<T> {
final T reference; //维护对象引用
final int stamp; //用于标志版本
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
private volatile Pair<V> pair;
....
/**
* expectedReference :更新之前的原始值
* newReference : 将要更新的新值
* expectedStamp : 期待更新的标志版本
* newStamp : 将要更新的标志版本
*/
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair; //获取当前pair
return
expectedReference == current.reference && //原始值等于当前pair的值引用,说明值未变化
expectedStamp == current.stamp && // 原始标记版本等于当前pair的标记版本,说明标记未变化
((newReference == current.reference &&
newStamp == current.stamp) || // 将要更新的值和标记都没有变化
casPair(current, Pair.of(newReference, newStamp))); // cas 更新pair
}
}
其实ABA的问题,update的值如果一直是递增的,就不会有ABA的问题,ABA问题的核心是update值变成另外的值又变回来,所以造成ABA,如果update值一直递增或者递减,就不会有ABA的问题,比如update值只能比当前值增加,就不会有ABA的问题。
8.AtomicLongArray 类,
实际也是通过要修改的数组元素的Index ,计算出相对于起始地址的偏移量,通过base+offset得到要修改的地址,通过cas来原子的修改
public class AtomicLongArray implements java.io.Serializable {
private static final long serialVersionUID = -2308431214976778248L;
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(long[].class);
private static final int shift;
private final long[] array;
static {
int scale = unsafe.arrayIndexScale(long[].class);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
shift = 31 - Integer.numberOfLeadingZeros(scale);
}
private long checkedByteOffset(int i) {
if (i < 0 || i >= array.length)
throw new IndexOutOfBoundsException("index " + i);
return byteOffset(i);
}
private static long byteOffset(int i) {
return ((long) i << shift) + base;
}
/**
* Creates a new AtomicLongArray of the given length, with all
* elements initially zero.
*
* @param length the length of the array
*/
public AtomicLongArray(int length) {
array = new long[length];
}
public final long getAndSet(int i, long newValue) {
return unsafe.getAndSetLong(array, checkedByteOffset(i), newValue);
}
9.AtomicIntegerFieldUpdater用于原子的修改某个对象的某个Integer字段, AtomicIntegerFieldUpdater 是一个抽象类,要通过newUpdater静态函数来创建一个对象,newUpdater需要指定要原子修改的field的名称,更新类的字段必须使用public volatile修饰。AtomicIntegerFieldUpdater根据反射,通过field名字拿到该field对应Field对象,通过Field对象拿到field的地址(this.offset = U.objectFieldOffset(field); U 是unsafe对象),原子的修改field的值。
public abstract class AtomicIntegerFieldUpdater<T> {
/**
* Creates and returns an updater for objects with the given field.
* The Class argument is needed to check that reflective types and
* generic types match.
*
* @param tclass the class of the objects holding the field
* @param fieldName the name of the field to be updated
* @param <U> the type of instances of tclass
* @return the updater
* @throws IllegalArgumentException if the field is not a
* volatile integer type
* @throws RuntimeException with a nested reflection-based
* exception if the class does not hold field or is the wrong type,
* or the field is inaccessible to the caller according to Java language
* access control
*/
@CallerSensitive
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>
(tclass, fieldName, Reflection.getCallerClass());
}
/**
* Protected do-nothing constructor for use by subclasses.
*/
protected AtomicIntegerFieldUpdater() {
}
private static final class AtomicIntegerFieldUpdaterImpl<T>
extends AtomicIntegerFieldUpdater<T> {
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private final long offset;
/**
* if field is protected, the subclass constructing updater, else
* the same as tclass
*/
private final Class<?> cclass;
/** class holding the field */
private final Class<T> tclass;
AtomicIntegerFieldUpdaterImpl(final Class<T> tclass,
final String fieldName,
final Class<?> caller) {
final Field field;
final int modifiers;
try {
field = AccessController.doPrivileged(
new PrivilegedExceptionAction<Field>() {
public Field run() throws NoSuchFieldException {
return tclass.getDeclaredField(fieldName);
}
});
modifiers = field.getModifiers();
sun.reflect.misc.ReflectUtil.ensureMemberAccess(
caller, tclass, null, modifiers);
ClassLoader cl = tclass.getClassLoader();
ClassLoader ccl = caller.getClassLoader();
if ((ccl != null) && (ccl != cl) &&
((cl == null) || !isAncestor(cl, ccl))) {
sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
}
} catch (PrivilegedActionException pae) {
throw new RuntimeException(pae.getException());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
if (field.getType() != int.class)
throw new IllegalArgumentException("Must be integer type");
if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
// Access to protected field members is restricted to receivers only
// of the accessing class, or one of its subclasses, and the
// accessing class must in turn be a subclass (or package sibling)
// of the protected member's defining class.
// If the updater refers to a protected field of a declaring class
// outside the current package, the receiver argument will be
// narrowed to the type of the accessing class.
this.cclass = (Modifier.isProtected(modifiers) &&
tclass.isAssignableFrom(caller) &&
!isSamePackage(tclass, caller))
? caller : tclass;
this.tclass = tclass;
this.offset = U.objectFieldOffset(field);
}
10.AtomicReferenceFieldUpdater和AtomicIntegerFieldUpdater类似,原子的修改某个对象的引用类型的成员变量的值。
11.AtomicReferenceFieldUpdater 和 AtomicReference的区别在于,AtomicReferenceFieldUpdater通常用于对于同一个对象中某个字段做原子更新,只有一个对象。AtomicReference是原子的更新引用(对象),会有多个对象。