无锁工具类

2021/01/08 posted in  并发工具

对于简单的原子性问题,JDK包装了一系列的实现原子类,相对于互斥方案,无锁类性能高,解决了互斥问题,没有加锁和解锁的操作,以及对线程切换的性能损耗

无锁方案实现原理

硬件支持,CPU为了解决并发问题,提供CAS指令,CAS指令包含3个参数:共享变量内存地址A,用于比较的值B,共享变量的新值C,只有当A地址的值为B时才能将A地址的值设置为C,作为CPU的一条指令CAS本身就能保证原子性

Java 原子类实现 count += 1

使用 AtomicLong 的 getAndIncrement() 方法实现


final long getAndIncrement() {
  // this 和 valueOffSet 唯一确定共享变量的内存地址
  return unsafe.getAndAddLong(
    this, valueOffset, 1L);
}

unsafe.getAndAddLong 方法首先读取内存中的值,之后循环调用compareAndSwapLong() 方法来尝试设置共享变量的值,直到成功为止。


public final long getAndAddLong(
  Object o, long offset, long delta){
  long v;
  do {
    // 读取内存中的值
    v = getLongVolatile(o, offset);
  } while (!compareAndSwapLong(
      o, offset, v, v + delta));
  return v;
}
//原子性地将变量更新为x
//条件是内存中的值等于expected
//更新成功则返回true
native boolean compareAndSwapLong(
  Object o, long offset, 
  long expected,
  long x);

CAS 模板代码

do {
  // 获取当前值
  oldV = xxxx;
  // 根据当前值计算新值
  newV = ...oldV...
}while(!compareAndSet(oldV,newV);

原子类概览

JDK中的原子类很丰富,可以分类为原子化的基本类型原子化的引用类型原子化数组原子化对象属性更新器原子化累加 五个类别提供的方法基本相似,并且都有若干的原子类

原子化的基本类型

相关实现有 AtomicBoolean、AtomicInteger 和 AtomicLong,提供的方法主要有以下这些

    getAndIncrement() //原子化i++
    getAndDecrement() //原子化的i--
    incrementAndGet() //原子化的++i
    decrementAndGet() //原子化的--i
    //当前值+=delta,返回+=前的值
    getAndAdd(delta) 
    //当前值+=delta,返回+=后的值
    addAndGet(delta)
    //CAS操作,返回是否成功
    compareAndSet(expect, update)
    //以下四个方法
    //新值可以通过传入func函数来计算
    getAndUpdate(func)
    updateAndGet(func)
    getAndAccumulate(x,func)
    accumulateAndGet(x,func)

原子化的对象引用类型

相关实现有 AtomicReference、AtomicStampedReference 和 AtomicMarkableReference,利用它们可以实现对象引用的原子化更新
AtomicStampedReference 和 AtomicMarkableReference 这两个原子类可以解决 ABA 问题

原子化数组

相关实现有 AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray,利用这些原子类,我们可以原子化地更新数组里面的每一个元素。这些类提供的方法和原子化的基本数据类型的区别仅仅是:每个方法多了一个数组的索引参数

原子化对象属性更新

相关实现有 AtomicIntegerFieldUpdater、AtomicLongFieldUpdater 和 AtomicReferenceFieldUpdater,利用它们可以原子化地更新对象的属性,这三个方法都是利用反射机制实现的,对象属性必须是 volatile 类型的,只有这样才能保证可见性

原子化累加器

DoubleAccumulator、DoubleAdder、LongAccumulator 和 LongAdder,这四个类仅仅用来执行累加操作,相比原子化的基本数据类型,速度更快,但是不支持 compareAndSet() 方法