Java从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。
悲观锁与乐观锁
悲观锁,认为数据发生并发冲突的概率很大,所以读操作之前就上锁。synchronized关键字,ReentrantLock 都是悲观锁的典型例子。
对于乐观锁,认为数据发生并发冲突的概率比较小,所以读操作之前不上锁。等到写操作的时候,再判断数据在此期间是否被其 他线程修改了。如果被其他线程修改了,就把数据重新读出来,重复该过程;如果没有被修改,就写回去。判断数据是否被修改,同时写 回新值,这两个操作要合成一个原子操作,也就是CAS(Compare And Set)。
原子更新基本类型类
使用原子的方式更新基本类型,Atomic包提供了以下3个类。
❑ AtomicBoolean:原子更新布尔类型。
❑ AtomicInteger:原子更新整型。
❑ AtomicLong:原子更新长整型。
以上3个类提供的方法类似以AtomicInteger为例进行讲解,AtomicInteger的常用方法如下。
❑ int addAndGet(int delta):以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。
❑ boolean compareAndSet(int expect,int update):如果输入的数值等于预期值,则以原子方式将该值设置为输入的值。
❑ int getAndIncrement():以原子方式将当前值加1,注意,这里返回的是自增前的值。
❑ void lazySet(int newValue):最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
❑ int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值。
public class AtomicIntegerTest { static AtomicInteger ai = new AtomicInteger(1); public static void main(String[] args) { System.out.println(ai.getAndIncrement()); System.out.println(ai.get()); } }
Unsafe的CAS
CAS函数,其实是封装的Unsafe类中的一个native函 数,如下所示。
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
AtomicInteger封装过的compareAndSet有两个参数。
第一个参数e xpect是指变量的旧值(是读出来的值,写回去的时候,希望没有被其 他线程修改,所以称为expect);
第二个参数update是指变量的新值 (修改过的,希望写入的值)。
当expect等于变量当前的值时,说明 在修改的期间,没有其他线程对此变量进行过修改,所以可以成功写 入,变量被更新为update,返回true;否则返回false。
Unsafe类是整个Concurrent包的基础,里面所有函数都是native 的。具体到compareAndSwapInt函数,如下所示。
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
该函数有4个参数。在前两个参数中,第一个是对象(也就是 AtomicInteger 对象),第二个是对象的成员变量(也就是AtomictInteger里面包的int变量value),后两个参数保持不变。 要特别说明一下第二个参数,它是一个long型的整数,经常被称为xxxOffset,意思是某个成员变量在对应的类中的内存偏移量(该变 量在内存中的位置),表示该成员变量本身。在Unsafe中专门有一个函数,把成员变量转化成偏移量,如下所示。
public native long objectFieldOffset(Field var1);
所有调用CAS的地方,都会先通过这个函数把成员变量转换成一个 Offset。以AtomicInteger为例:
从上面代码可以看到,无论是Unsafe还是valueOffset,都是静态 的,也就是类级别的,所有对象共用的。 在转化的时候,先通过反射(getDeclaredField)获取value成员 变量对应的Field对象,再通过objectFieldOffset函数转化成valueOffset。此处的valueOffset就代表了value变量本身,后面执行CAS操作 的时候,不是直接操作value,而是操作valueOffset。
参考: Java并发编程的艺术 7.1 原子更新基本类型类
Java并发实现原理:Atomic类