java并发编程系列-无锁工具类:并发包下的原子类

215 阅读3分钟

我们在前面的示例 2-6 中的累加器,为了保证线程安全,我们给 add 方法和 get 方法分别加了锁,虽然保证了线程安全,但是在竞争激烈的情况下,频繁的加锁解锁是一件十分耗费性能的事情。其实为了解决这类原子性问题,JDK 1.5 以后提供了大量的原子类,这是一种无锁方案。

示例:5-1
public class CountDemo {
  private AtomicLong count = new AtomicLong(0);

  public void add() {
      count.getAndIncrement();
  }

  public long get() {
    return count.get();
  }

}

我们可以查看 getAndIncrement() 源码:

示例:5-2
public final long getAndIncrement() {
    return unsafe.getAndAddLong(this, valueOffset, 1L);
}

进入 Unsafe 类:

示例:5-3
public final long getAndAddLong(Object var1, long var2, long var4) {
    long var6;
    do {
        var6 = this.getLongVolatile(var1, var2);
    } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

    return var6;
}

Unsafe 类是和系统直接交互的类,它可以访问系统内存资源,管理内存资源。它的功能包括内存操作、CAS、Class 相关、对象操作、线程调度、系统信息获取、内存屏障、数组操作。

compareAndSwapLong() 方法从字面意思我们也知道,它就是 CAS,CAS 指令包含三个参数:共享变量的内存地址 A,用于比较的值 B 和新值 C,只有当 A 和 B 相等时,才会将内存中的值 A 更新为 C。

示例:5-4
public final class Unsafe {
      private static final Unsafe theUnsafe;
      private Unsafe() {}
      @CallerSensitive
      public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        // 类加载器为jdk的类加载器加载时才行,否则抛出异常
        if(!VM.isSystemDomainLoader(var0.getClassLoader())) {    
          throw new SecurityException("Unsafe");
        } else {
          return theUnsafe;
        }
      }
}

Unsafe 类为一单例实现,提供静态方法 getUnsafe 获取 Unsafe 实例,当且仅当调用 getUnsafe 方法的类为引导类加载器所加载时才合法,否则抛出 SecurityException 异常。并发包下的原子类的实现都依赖 Unsafe, 对 Unsafe 感兴趣的同学可以自行研究该类。

原子类脑图总结

1570957716841

基本数据类型

这些原子类额用法也相对简单,记住常用的方法即可:

getAndIncrement() //  i++
incrementAndGet() //  ++i
getAndDecrement() //  i--
decrementAndGet() //  --i

getAndAdd(delta)  //当前值 +=delta

addAndGet(delta)//当前值 +=delta

原子数组

相关实现有 AtomicIntegerArray、AtomicLongArray,利用这些原子类,我们可以原子化地更新数组里面的每个元素。

对象属性更新

它们可以原子化地更新对象的属性,这三个方法都是利用反射机制实现的,创建更新器的方法如下:

示例:5-5
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);
 }

对象引用

利用它们可以实现对象引用的原子化更新,接下来我会以 AtomicReference 写一个 Demo。

示例:5-6
public class AtomicReferenceTest {

    public static void main(String[] args) {
        AtomicReference<Student> ar = new AtomicReference<Student>();
        Student student = new Student("张三", 20);
        ar.set(student);
        Student student1 = new Student("李四", 19);
        ar.compareAndSet(student, student1);

        System.out.println(ar.get().getName());
        System.out.println(ar.get().getAge());
    }
}

class Student {
    private String name;
    private int age;

    public Student(String name, int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}

上述代码首先创建了一个 Student 对象,然后把 Student 对象设置进 AtomicReference 对象中,然后调用 compareAndSet 方法,该方法就是通过通过 CAS 操作设置 ar。如果 ar 的值为 student 的话,则将其设置为 student1。运行上面的代码后的输出结果如下:

李四
19