1. 无 锁类 的原理 详 解
1.1.CAS
CAS算法的过程是这样:它包含3个参数CAS(V,E,N)。V表示要更新的变量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。CAS操作是抱着乐观的态度进行的,它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS操作即时没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。
1.2.CPU 指令
CAS操作是在一条cpu指令中完成。具有原子性,线程安全
cmpxchg /* accumulator = AL, AX, or EAX, depending on whether a byte, word, or doubleword comparison is being performed */ if(accumulator == Destination) { ZF = 1; Destination = Source; } else { ZF = 0; accumulator = Destination; }
2. 无 锁类
2.1AtomicInteger
AtomicInteger和Integer一样,都继承与Number类
public class AtomicInteger extends Number implements java.io.Serializable
AtomicInteger里面有很多CAS操作,典型的有:
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
这里来解释一下unsafe.compareAndSwapInt方法,他的意思是,对于this这个类上的偏移量为valueOffset的变量值如果与期望值expect相同,那么把这个变量的值设为update。
其实偏移量为valueOffset的变量就是value
static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }
我们此前说过,CAS是有可能会失败的,但是失败的代价是很小的,所以一般的实现都是在一个无限循环体内,直到成功为止。
public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } }
Unsafe类中使用
从类名就可知,Unsafe操作是非安全的操作,比如:
- 根据偏移量设置值(在刚刚介绍的AtomicInteger中已经看到了这个功能)
- park()(把这个线程停下来,在以后的Blog中会提到)
- 底层的CAS操作
非公开API,在不同版本的JDK中,可能有较大差异
2.2 AtomicReference(模板类)、AtomicStampedReference、AtomicIntegerArray(无锁数组)不一一列举
2.3 AtomicIntegerFieldUpdater(普通变量也享受原子)
2.3.1. 概述
让普通变量也享受原子操作
2.3.2. 主要接口
AtomicIntegerFieldUpdater.newUpdater()
incrementAndGet()
2.3.3. 小 说 明
1.Updater只能修改它可见范围内的变量。因为Updater使用反射得到这个变量。如果变量不可见,就会出错。
比如如果score申明为private,就是不可行的。
2.为了确保变量被正确的读取,它必须是volatile类型的。如果我们原有代码中未申明这个类型,那么简单得
申明一下就行,这不会引起什么问题。
3. 由于CAS操作会通过对象实例中的偏移量直接进行赋值,因此,它不支持static字段(Unsafe.
objectFieldOffset()不支持静态变量)。