这篇说说java.util.concurrent.atomic包里的类,总共12个,网上有很多文章解析这几个类,这里挑些重点说说。

这12个类可以分为三组:

1. 普通类型的原子变量

2. 数组类型的原子变量

3. 域更新器

普通类型的原子变量的6个,

1. 其中AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference分别对应boolean, int,  long, object完成基本的原子操作

2. AtomicMarkableReference, AtomicStampedReference是AtomicReference的功能增强版本,前者可以把引用跟一个boolean绑定,后者可以把引用和一个int型的版本号绑定来完成时间戳的功能。

AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference这几个类的结构都相似,有几个特点

1. 底层都是采用sun.misc.Unsafe类来完成实际的CAS操作

2. 使用sun.misc.Unsafe直接操作内存对象来完成类似反射机制的对象属性存取能力

3. volatile类型的value值保存状态

4. 原子的get(), set()方法

5. 基本的compareAndSet方法完成CAS操作

6. weakCompareAndSet,弱化版本的CAS操作,这是API设计是预留地差异化接口,但是目前没有实现,目前和compareAndSet是一样的功能

7. getAndSet方法是利用CAS操作无锁地完成读取并设置的功能

8. lazySet方法优化设置,lazySet的使用看这篇 聊聊高并发(十八)理解AtomicXXX.lazySet方法

原子变量在并发编程中是基本的工具,可以用来实现非阻塞的数据结构和构建相关的基础构件。有几种基本的用法:

1. 安全的计数器

2. compareAndSet方法可以实现“滤网”的功能,找到第一个成功操作的线程,从而做一些操作,可以看看自旋锁相关的实现

3. compareAndSet方法可以实现“判断操作是否成功”的功能,这里会有ABA的问题,可以采用AtomicStampedReference来避免ABA问题

4. getAndSet方法可以实现完全的“设置并返回之前值”的功能

5. AtomicBoolean作为一种二元状态可以用来作为“开关”,实现找到一个打开开关的线程。比如 if(b.compareAndSet(false, true)){// dosomething}

来看几个典型的操作

 
  1. public class AtomicBoolean implements java.io.Serializable {

  2. private static final long serialVersionUID = 4654671469794556979L;

  3. // setup to use Unsafe.compareAndSwapInt for updates

  4. private static final Unsafe unsafe = Unsafe.getUnsafe();

  5. private static final long valueOffset;

  6. // 使用Unsafe直接操作内存的方式设置属性的值

  7. static {

  8. try {

  9. valueOffset = unsafe.objectFieldOffset

  10. (AtomicBoolean.class.getDeclaredField("value"));

  11. } catch (Exception ex) { throw new Error(ex); }

  12. }

  13. // 使用volatile变量来保存状态

  14. private volatile int value;

  15. // 使用Unsafe的compareAndSwapXXX方法完成底层的CAS操作

  16. public final boolean compareAndSet(boolean expect, boolean update) {

  17.         int e = expect ? 1 : 0;

  18.         int u = update ? 1 : 0;

  19.         return unsafe.compareAndSwapInt(this, valueOffset, e, u);

  20.     }

  21. // 无锁地实现“设置并返回之前值”的功能,无锁的特点就是轮询加CAS操作

  22. public final boolean getAndSet(boolean newValue) {

  23.         for (;;) {

  24.             boolean current = get();

  25.             if (compareAndSet(current, newValue))

  26.                 return current;

  27.         }

  28.     }

  29. // 优化volatie变量的写,再不需要保证可见性的场景下使用lazySet来优化,减少内存屏障

  30. public final void lazySet(boolean newValue) {

  31.         int v = newValue ? 1 : 0;

  32.         unsafe.putOrderedInt(this, valueOffset, v);

  33.     }

AtomicMarkableReference和AtomicStampedReference都是对AtomicReference的增强,内部使用了不可变对象来保存一个二元状态<Reference, XXX>,当原子设置时,就创建新的对象,并把volaitle引用指向最新的不可变对象。更多内容请查看聊聊高并发(十二)分析java.util.concurrent.atomic.AtomicStampedReference源码来看如何解决CAS的ABA问题

AtomicMarkableReference可以用来标记对象,常用来构建数据结构中表示节点,可以用boolean表示节点是否被删除

 
  1. public class AtomicMarkableReference<V> {

  2. private static class Pair<T> {

  3. final T reference;

  4. final boolean mark;

  5. private Pair(T reference, boolean mark) {

  6. this.reference = reference;

  7. this.mark = mark;

  8. }

  9. static <T> Pair<T> of(T reference, boolean mark) {

  10. return new Pair<T>(reference, mark);

  11. }

  12. }

  13. private volatile Pair<V> pair;

  14. public AtomicMarkableReference(V initialRef, boolean initialMark) {

  15. pair = Pair.of(initialRef, initialMark);

  16. }

  17. public boolean compareAndSet(V       expectedReference,

  18.                                  V       newReference,

  19.                                  boolean expectedMark,

  20.                                  boolean newMark) {

  21.         Pair<V> current = pair;

  22.         return

  23.             expectedReference == current.reference &&

  24.             expectedMark == current.mark &&

  25.             ((newReference == current.reference &&

  26.               newMark == current.mark) ||

  27.              casPair(current, Pair.of(newReference, newMark)));

  28.     } 

数组类型的原子变量有3个: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray, 它们是普通原子变量的数组版本,可以完成对数组中元素的原子操作。

基本的方法和普通原子类型类似,这里就不重复说了,提一下利用Unsafe读取数组元素的方法

1. 利用Unsafe.arrayBaseOffset得到数组的第一个元素的偏移量,因为有对象头的存在,所以offset不是从0开始

2. 利用Unsafe.arrayIndexScale得到数组中元素的长度

3. 利用移位操作代替乘法提高效率。所以先计算shift,比如8字节长度的元素,需要左移3位,相当与2的3次幂,4字节长度的元素左移2位,相当于2的2次幂

4. 计算数组中元素的实际位置 = index << shift + base,说白了,就是  index * 元素长度  + base

 
  1. public class AtomicIntegerArray implements java.io.Serializable {

  2. private static final Unsafe unsafe = Unsafe.getUnsafe();

  3. // 获得数组第一个元素的偏移量offset,因为有对象头的存在,所以offset不是从0开始的

  4.  private static final int base = unsafe.arrayBaseOffset(int[].class);

  5. // 移位操作的基数,用移位操作代替乘法

  6. private static final int shift;

  7. private final int[] array;

  8. static {

  9. // 获取数组元素的长度,对于int[]数组, scale = 4

  10.  int scale = unsafe.arrayIndexScale(int[].class);

  11. // 如果长度不是为2的幂,就报错

  12.  if ((scale & (scale - 1)) != 0)

  13. throw new Error("data type scale not a power of two");

  14. // 31 - Integer.numberOfLeadingZeros(scale) 相当于求floor(log2(x)),这里为2,如果是Long,就是3

  15. // 其实就是用移位操作代替乘法,比如4字节长度,就要左移2位,8字节长度,就要左移3位。左移1位 = 乘 2

  16.  shift = 31 - Integer.numberOfLeadingZeros(scale);

  17. }

  18. private long checkedByteOffset(int i) {

  19. if (i < 0 || i >= array.length)

  20. throw new IndexOutOfBoundsException("index " + i);

  21. return byteOffset(i);

  22. }

  23. // 用移位操作代替乘法,实际上求的是数组的第i个元素的偏移量,方便定位到数组元素的内存地址

  24. private static long byteOffset(int i) {

  25. return ((long) i << shift) + base;

  26. }

最后看看域更新器,有三个: AtomicIntegerFieldUpdate,  AtomicLongFieldUpdate,  AtomicReferenceFieldUpdate

域更新器是一种优化手段,它提供了现有volatile域的一种基于反射的视图,从而能对现有volatile域进行CAS操作,我们知道volatile字段只保证可见性,但是不保证原子性,

如果要想对volatile字段进行CAS操作,就要用到域更新器。它的好处是可以让volatile字段具备原子变量的能力,而不需要实际创建这么多的原子变量,毕竟volatile比起原子变量来说还是轻量级的。

域更新器没有提供对外的构造函数,它需要利用工厂方法的方式来创建,提供一个newUpdater(xxx)方法来返回一个新建的域更新器对象。

1. tclass指的是字段所在类的Class类型

2. vclass指的是字段的Class类型,需要注意的是字段必须是volatile标示的,不然会抛出异常

3. filedName字段名

4. 调用者的类型,可以用Reflection.getCallerClass()获得

 
  1. public static <U, W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass, Class<W> vclass, String fieldName) {

  2. return new AtomicReferenceFieldUpdaterImpl<U,W>(tclass,

  3. vclass,

  4. fieldName,

  5. Reflection.getCallerClass());

  6. }

  7.   AtomicReferenceFieldUpdaterImpl(Class<T> tclass,

  8.                                         Class<V> vclass,

  9.                                         String fieldName,

  10.                                         Class<?> caller) {

  11.             Field field = null;

  12.             Class fieldClass = null;

  13.             int modifiers = 0;

  14.             try {

  15.                 field = tclass.getDeclaredField(fieldName);

  16.                 modifiers = field.getModifiers();

  17.                 sun.reflect.misc.ReflectUtil.ensureMemberAccess(

  18.                     caller, tclass, null, modifiers);

  19.                 sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);

  20.                 fieldClass = field.getType();

  21.             } catch (Exception ex) {

  22.                 throw new RuntimeException(ex);

  23.             }

  24.             if (vclass != fieldClass)

  25.                 throw new ClassCastException();

  26.             if (!Modifier.isVolatile(modifiers))

  27.                 throw new IllegalArgumentException("Must be volatile type");

  28.             this.cclass = (Modifier.isProtected(modifiers) &&

  29.                            caller != tclass) ? caller : null;

  30.             this.tclass = tclass;

  31.             if (vclass == Object.class)

  32.                 this.vclass = null;

  33.             else

  34.                 this.vclass = vclass;

  35.             offset = unsafe.objectFieldOffset(field);

  36.         }

AtomicIntegerFieldUpdate这些更新器的接口和原子变量一致,都提供了compareAndSet操作,getAndSet操作等,这里不重复说。举个例子看看如何使用域更新器

1. Node类有一个volatile类型的next字段,它没有使用AtomicReference,使用了更轻量级的volatile

2. 如果想对volatile类型的next做CAS操作,就要创建域更新器AtomicReferenceFieldUpdater

 
  1. private class Node<E>{

  2. private final E item;

  3. private volatile Node<E> next;

  4. public Node(E item){

  5. this.item = item;

  6. }

  7. }

  8. private static AtomicReferenceFieldUpdater<Node, Node> nextUpdate = AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "next");

聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类相关推荐

  1. 聊聊高并发(二十二)解析java.util.concurrent各个组件(四) 深入理解AQS(二)

    上一篇介绍了AQS的基本设计思路以及两个内部类Node和ConditionObject的实现 聊聊高并发(二十一)解析java.util.concurrent各个组件(三) 深入理解AQS(一) 这篇 ...

  2. 聊聊高并发(二十九)解析java.util.concurrent各个组件(十一) 再看看ReentrantReadWriteLock可重入读-写锁

    上一篇聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁 讲了可重入读写锁的基本情况和主要的方法,显示了如何 ...

  3. 聊聊高并发(二十五)解析java.util.concurrent各个组件(七) 理解Semaphore

    前几篇分析了一下AQS的原理和实现,这篇拿Semaphore信号量做例子看看AQS实际是如何使用的. Semaphore表示了一种可以同时有多个线程进入临界区的同步器,它维护了一个状态表示可用的票据, ...

  4. 聊聊高并发(二十九)解析java.util.concurrent各个组件(十一) 再看看ReentrantReadWriteLock可重入读-写锁...

    上一篇聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁 讲了可重入读写锁的基本情况和基本的方法,显示了怎样 ...

  5. 聊聊高并发(二十一)解析java.util.concurrent各个组件(三) 深入理解AQS(一)

    AQS是AbstractQueuedSynchronizer的缩写,AQS是Java并包里大部分同步器的基础构件,利用AQS可以很方便的创建锁和同步器.它封装了一个状态,提供了一系列的获取和释放操作, ...

  6. 聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁

    这篇讲讲ReentrantReadWriteLock可重入读写锁,它不仅是读写锁的实现,并且支持可重入性. 聊聊高并发(十五)实现一个简单的读-写锁(共享-排他锁) 这篇讲了如何模拟一个读写锁. 可重 ...

  7. 聊聊高并发(二十七)解析java.util.concurrent各个组件(九) 理解ReentrantLock可重入锁

    这篇讲讲ReentrantLock可重入锁,JUC里提供的可重入锁是基于AQS实现的阻塞式可重入锁.这篇 聊聊高并发(十六)实现一个简单的可重入锁 模拟了可重入锁的实现.可重入锁的特点是: 1. 是互 ...

  8. 聊聊高并发(十七)解析java.util.concurrent各个组件(一) 了解sun.misc.Unsafe类

    了解了并发编程中锁的基本原理之后,接下来看看Java是如何利用这些原理来实现各种锁,原子变量,同步组件的.在开始分析java.util.concurrent的源代码直接,首先要了解的就是sun.mis ...

  9. 聊聊高并发(三十)解析java.util.concurrent各个组件(十二) 理解CyclicBarrier栅栏

    这篇讲讲CyclicBarrier栅栏,从它的名字可以看出,它是可循环使用的.它的功能和CountDownLatch类似,也是让一组线程等待,然后一起开始往下执行.但是两者还是有几个区别 1. 等待的 ...

最新文章

  1. 12 Java程序员面试宝典视频课程之面向对象
  2. 数据链路层和传输层可靠传输是否重复多余
  3. 基于struts2的登录系统
  4. BZOJ 2095 [POI2010]Bridges (最大流、欧拉回路)
  5. JProfiler9安装 监控Tomcat
  6. 嵌入式linux面试题解析(二)——C语言部分三
  7. 【HAOI2015】按位或【Min-Max容斥】【FWT】
  8. 为啥我的Python这么慢 - 项查找 (二)
  9. JUnit5基本用法
  10. Homebrew 插件自启动
  11. 【操作系统/OS笔记10】进程/线程的调度原则、调度算法、实时调度、多处理器调度、优先级反转
  12. qt之解决qtableview加载百万行数据卡顿问题
  13. 系统地编译Hi3519过程及其处理问题思路
  14. 条码专题--条码技术应用
  15. 购书网站前端实现(HTML+CSS+JavaScript)
  16. 基于cuda10.0的pytorch深度学习环境配置
  17. 当元宇宙时代来临,才真正让这些新技术跳出了互联网的牵绊
  18. 未明学院:来自券商研究所搬砖狗的自白
  19. SpringBoot集成SpringSecurity(二) 个性化登录配置(remember-me mongodb)
  20. delphi 龙年窗体 恭喜发财

热门文章

  1. java实验原理和图例_图例解析JDK,JRE,JVM概念及使用
  2. requirednew基于xml配置日志不回滚_Mybatis 系列 4:引入日志框架
  3. mysql 5.6.23 源码包安装报错_CentOS6.5_64bit下编译安装MySQL-5.6.23
  4. 又见程序员精神——有感于谷歌一天一夜开发春运交通图
  5. html正则表达式的书写,前端正则表达式书写及常用的方法
  6. linux 循环每个月,SHELL脚本每月最后一天判断
  7. android q测试机型,小米9安卓Q系统刷机包开启测试 小米Android Q适配机型一览
  8. ObjectFactory对象工厂类
  9. 关于举办XX学院“XX杯”网页设计大赛的通知
  10. JAVAEE框架之Spring注解