Java并发_CAS和JUC原子类(5)

CAS

加锁属于悲观操作,即假设一定会发生并发冲突,开销较大

对于细粒度的操作例如自增,则可以使用开销较小的乐观操作,即假设不会发生并发冲突,通过冲突检测来判断是否有其他线程的干扰,如果被干扰了再决定如何处理,例如 CAS (Compare And Swap)

CAS 操作有三个操作数,需要读写的内存位置 V、进行比较的预期原值 A、要写入的新值 B,只有当 V 值等于 A 值时,才会通过原子的方式将 V 值更新为 B 值

多个线程同时执行 CAS 操作,总有一个线程能执行成功,其他失败的则可通过自旋反复执行,直到所有线程都执行成功,这涉及到需要每次执行获取到变量的值都是最新的,所以要使用 volatile 来修饰变量,保证变量的可见性

compare 和 swap 过程的原子性由 CPU 硬件提供支持,Java 5 之后 JVM 支持了 CAS 操作,Unsafe 类提供了可以直接操作内存的方法,“Java 中的指针”

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//参数依次为要修改的对象、对象的内存地址偏移量、修改前期望的值、修改后期望的值
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);
//+= 操作,传入要修改的对象、对象内存偏移量、增量
public final long getAndAddLong(Object o, long offset, long delta) {
  long v;
  do {
    //修改前获取旧值
    v = getLongVolatile(o, offset);
  //失败重试,CAS自旋操作
  } while (!compareAndSwapLong(o, offset, v, v + delta));
  return v;
}

CAS 可能出现 ABA 问题,即一个变量第一次读取为 A,在准备赋值时第二次读取还是 A,并不能说明该变量在这个过程中没有发生改变,可能是中途被改为 B 然后后变回了 A,但 CAS 会误认为该变量没有被改变过

可以通过 JUC 下带有标记的原子引用类 AtomicStampedReference 来控制变量值的版本,AtomicStampedReference 内部维护了一个 int stamp,若对象被修改,除了修改 value 值,还要更新 stamp,以此来记录变量的版本,解决了 ABA 问题

除了 ABA 问题,CAS 只能保证一个变量的原子操作,可通过 JUC 下的 AtomicReference 将需要保证原子性的多个变量放入一个对象来进行 CAS 操作

若 CAS 自旋长时间不成功,也会给 CPU 带来负担,需要根据具体情况限制循环次数或设置超时时间

JUC 原子类

分类

可将 JUC 包下的原子类分为下面的五种类型

基本类型,AtomicBoolean、AtomicInteger、AtomicLong

数组类型,AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

引用类型,AtomicReference、AtomicStampedReference、AtomicMarkableReference

对象属性更新,AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater

Java 8 新增,DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder

AtomicLong

基本类型 AtomicInteger、AtomicLong、AtomicBoolean 的原理和用法相似,都是提供单个变量的原子访问功能,以 AtomicLong 为代表进行学习

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//构造方法
AtomicLong() //默认值为0
AtomicLong(long initialValue) //通过普通long变量构造
//主要方法
//将delta加到当前值,并返回相加前的值
final long getAndAdd(long delta) 
//将delta加到当前值,并返回相加后的值
final long addAndGet(long delta)
//++num
final long incrementAndGet()
//num++
final long getAndIncrement()
//--num
final long decrementAndGet()
//num--
final long getAndDecrement()
//如果当前值==expect,则将该值设置为update,成功返回true失败返回false,且不修改原值
final boolean compareAndSet(long expect, long update)

例如非原子性的自增操作 ++,可以直接通过加锁来保证原子性,但也可以通过开销较小的 AtomicLong 类提供的 incrementAndGet 方法来保证线程安全

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Test extends Thread {
  public static AtomicLong count = new AtomicLong(0);
  @Override
  public void run() {
    count.incrementAndGet();
    System.out.println(
      Thread.currentThread().getName() + " count = " + count);
  }
  public static void main(String[] args) {
    Test t1 = new Test();
    Test t2 = new Test();
    t1.start();
    t2.start();
    try {
      Thread.sleep(3000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println(count);
  }
}
//Thread-0 count = 1
//Thread-1 count = 2
//2

JUC 包没有提供 AtomicFloat、AtomicDouble 的实现,可以通过 Float.floatToIntBits 方法和Float.intBitsToFloat 方法来转换,double 同理可通过 Double.doubleToLongBits 方法和Double.longBitsToDouble 方法来转换

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Test {
  public static void main(String[] args) {
    AtomicFloat af = new AtomicFloat(1.1f);
    af.addAndGet(0.1f);
    System.out.println(af.get()); //1.2
  }
}
class AtomicFloat {
  private AtomicInteger intBits;
  public AtomicFloat(float initialValue) {
    intBits = new AtomicInteger(Float.floatToIntBits(initialValue));
  }
  public final float addAndGet(float delta) {
    float expect;
    float update;
    do {
      expect = get();
      update = expect + delta;
    } while (!this.compareAndSet(expect, update));
    return update;
  }
  public final boolean compareAndSet(float expect, float update) {
    return intBits.compareAndSet(
      Float.floatToIntBits(expect), Float.floatToIntBits(update));
  }
  public final float get() {
    return Float.intBitsToFloat(intBits.get());
  }
}  

查看 AtomicLong 的源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class AtomicLong extends Number implements java.io.Serializable {
  //sun.misc.Unsafe类的CAS操作
  private static final Unsafe unsafe = Unsafe.getUnsafe();
  //判断当前的系统唤醒是否支持CAS
  static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
  private static native boolean VMSupportsCS8();

  //static代码块通过unsafe初始化value的内存地址valueOffset
  //使得后续操作可以直接通过内存地址进行
  static {
    try {
      valueOffset = unsafe.objectFieldOffset
        (AtomicLong.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
  }
  //value的内存地址
  private static final long valueOffset;
  //value通过volatile进行修饰保证变量的可见性
  private volatile long value;

  //构造函数
  public AtomicLong(long initialValue) {
    value = initialValue;
  }
	//num++操作方法
  public final long getAndIncrement() {
    return unsafe.getAndAddLong(this, valueOffset, 1L);
  }
}

AtomicLongArray

数组类型以 AtomicLongArray 为代表进行学习

1
2
3
4
5
//构造方法
AtomicLongArray(int length)    //指定长度
AtomicLongArray(long[] array)  //通过普通数组构造

//方法同AtomicLon类似,只是参数需要传入索引i
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import java.util.concurrent.atomic.AtomicLongArray;
public class Test {
  public static void main(String[] args) {
    long[] longArr = new long[] {10, 20, 30, 40, 50};
    AtomicLongArray ala = new AtomicLongArray(longArr);
    for (int i = 0; i < ala.length(); i++) {
      System.out.println(ala.get(i));
    }
  }
}

AtomicReference

引用类型以 AtomicReference 为代表进行学习

1
2
3
4
5
//构造方法
AtomicReference() //默认值为null
AtomicReference(V initialValue) //通过普通引用构造

//方法同AtomicLon类似
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Test {
  public static void main(String[] args){
    Person p1 = new Person(1);
    Person p2 = new Person(2);
    AtomicReference<Person> ar = new AtomicReference<>(p1);
    //如果ar的值为p1,则将其设置为p2
    ar.compareAndSet(p1, p2);
    Person p3 = ar.get();
    System.out.println(p3); //id=2
  }
}
class Person {
  volatile long id;
  public Person(long id) {
    this.id = id;
  }
  public String toString() {
    return "id=" + id;
  }
}

AtomicLongFieldUpdater

对象属性更新以 AtomicLongFieldUpdater 为代表进行学习,对指定类对象的 volatile long 属性进行操作,是基于反射实现的

1
2
3
4
//无法通过new创建对象
protected AtomicLongFieldUpdater()
//创建对象属性更新器
static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Test {
  public static void main(String[] args) {
    //通过newUpdater静态方法创建AtomicLongFieldUpdater对象
    //参数为类的Class对象和volatile long类型属性的名称
    AtomicLongFieldUpdater<Person> alfu =
      AtomicLongFieldUpdater.newUpdater(Person.class, "id");
    Person p = new Person(12345678L);
    alfu.compareAndSet(p, 12345678L, 1000);
    System.out.println(p);
  }
}
class Person {
  volatile long id;
  public Person(long id) {
    this.id = id;
  }
  public String toString() {
    return "id=" + id;
  }
}

查看 newUpdater 方法的源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//AtomicLongFieldUpdater是抽象类
//根据JVM是否支持CAS返回其子类CASUpdater或LockedUpdater
public static <U> AtomicLongFieldUpdater<U> newUpdater(
  Class<U> tclass, String fieldName) {
  Class<?> caller = Reflection.getCallerClass();
  if (AtomicLong.VM_SUPPORTS_LONG_CAS)
    return new CASUpdater<U>(tclass, fieldName, caller);
  else
    return new LockedUpdater<U>(tclass, fieldName, caller);
}

以上内容是玉山整理的笔记,如有错误还请指出