聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类
这篇说说java.util.concurrent.atomic包里的类,总共12个。网上有非常多文章解析这几个类。这里挑些重点说说。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSVRlcl9aQw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
这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}
来看几个典型的操作
public class AtomicBoolean implements java.io.Serializable {
    private static final long serialVersionUID = 4654671469794556979L;
    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
    // 使用Unsafe直接操作内存的方式设置属性的值
    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicBoolean.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }
    // 使用volatile变量来保存状态
    private volatile int value;
    // 使用Unsafe的compareAndSwapXXX方法完毕底层的CAS操作
    public final boolean compareAndSet(boolean expect, boolean update) {
        int e = expect ?
1 : 0;
        int u = update ? 1 : 0;
        return unsafe.compareAndSwapInt(this, valueOffset, e, u);
    }
    // 无锁地实现“设置并返回之前值”的功能,无锁的特点就是轮询加CAS操作
    public final boolean getAndSet(boolean newValue) {
        for (;;) {
            boolean current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }
    // 优化volatie变量的写,再不须要保证可见性的场景下使用lazySet来优化,降低内存屏障
    public final void lazySet(boolean newValue) {
        int v = newValue ? 1 : 0;
        unsafe.putOrderedInt(this, valueOffset, v);
    }
AtomicMarkableReference和AtomicStampedReference都是对AtomicReference的增强,内部使用了不可变对象来保存一个二元状态<Reference, XXX>,当原子设置时。就创建新的对象。并把volaitle引用指向最新的不可变对象。很多其它内容请查看聊聊高并发(十二)分析java.util.concurrent.atomic.AtomicStampedReference源代码来看怎样解决CAS的ABA问题
AtomicMarkableReference能够用来标记对象。经常使用来构建数据结构中表示节点。能够用boolean表示节点是否被删除
public class AtomicMarkableReference<V> {
    private static class Pair<T> {
        final T reference;
        final boolean mark;
        private Pair(T reference, boolean mark) {
            this.reference = reference;
            this.mark = mark;
        }
        static <T> Pair<T> of(T reference, boolean mark) {
            return new Pair<T>(reference, mark);
        }
    }
    private volatile Pair<V> pair;
    public AtomicMarkableReference(V initialRef, boolean initialMark) {
        pair = Pair.of(initialRef, initialMark);
    }
    public boolean compareAndSet(V       expectedReference,
                                 V       newReference,
                                 boolean expectedMark,
                                 boolean newMark) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&
            expectedMark == current.mark &&
            ((newReference == current.reference &&
              newMark == current.mark) ||
             casPair(current, Pair.of(newReference, newMark)));
    } 
数组类型的原子变量有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
public class AtomicIntegerArray implements java.io.Serializable {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // 获得数组第一个元素的偏移量offset,由于有对象头的存在,所以offset不是从0開始的
    private static final int base = unsafe.arrayBaseOffset(int[].class);
    // 移位操作的基数,用移位操作取代乘法
    private static final int shift;
    private final int[] array;
    static {
        // 获取数组元素的长度,对于int[]数组。 scale = 4
        int scale = unsafe.arrayIndexScale(int[].class);
        // 假设长度不是为2的幂,就报错
        if ((scale & (scale - 1)) != 0)
            throw new Error("data type scale not a power of two");
        // 31 - Integer.numberOfLeadingZeros(scale) 相当于求floor(log2(x)),这里为2。假设是Long,就是3
        // 事实上就是用移位操作取代乘法,比方4字节长度,就要左移2位,8字节长度,就要左移3位。左移1位 = 乘 2
        shift = 31 - Integer.numberOfLeadingZeros(scale);
    }
    private long checkedByteOffset(int i) {
        if (i < 0 || i >= array.length)
            throw new IndexOutOfBoundsException("index " + i);
        return byteOffset(i);
    }
    // 用移位操作取代乘法,实际上求的是数组的第i个元素的偏移量,方便定位到数组元素的内存地址
    private static long byteOffset(int i) {
        return ((long) i << shift) + base;
    }
最后看看域更新器,有三个: 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()获得
 public static <U, W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass, Class<W> vclass, String fieldName) {
        return new AtomicReferenceFieldUpdaterImpl<U,W>(tclass,
                                                        vclass,
                                                        fieldName,
                                                        Reflection.getCallerClass());
    }
  AtomicReferenceFieldUpdaterImpl(Class<T> tclass,
                                        Class<V> vclass,
                                        String fieldName,
                                        Class<?> caller) {
            Field field = null;
            Class fieldClass = null;
            int modifiers = 0;
            try {
                field = tclass.getDeclaredField(fieldName);
                modifiers = field.getModifiers();
                sun.reflect.misc.ReflectUtil.ensureMemberAccess(
                    caller, tclass, null, modifiers);
                sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
                fieldClass = field.getType();
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
            if (vclass != fieldClass)
                throw new ClassCastException();
            if (!Modifier.isVolatile(modifiers))
                throw new IllegalArgumentException("Must be volatile type");
            this.cclass = (Modifier.isProtected(modifiers) &&
                           caller != tclass) ? caller : null;
            this.tclass = tclass;
            if (vclass == Object.class)
                this.vclass = null;
            else
                this.vclass = vclass;
            offset = unsafe.objectFieldOffset(field);
        }
AtomicIntegerFieldUpdate这些更新器的接口和原子变量一致。都提供了compareAndSet操作,getAndSet操作等。这里不反复说。举个样例看看怎样使用域更新器
1. Node类有一个volatile类型的next字段,它没有使用AtomicReference。使用了更轻量级的volatile
2. 假设想对volatile类型的next做CAS操作。就要创建域更新器AtomicReferenceFieldUpdater
private class Node<E>{
		private final E item;
		private volatile Node<E> next;
		public Node(E item){
			this.item = item;
		}
	}
	private static AtomicReferenceFieldUpdater<Node, Node> nextUpdate = AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "next");
聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类的更多相关文章
- 聊聊高并发(二十五)解析java.util.concurrent各个组件(七) 理解Semaphore
		前几篇分析了一下AQS的原理和实现.这篇拿Semaphore信号量做样例看看AQS实际是怎样使用的. Semaphore表示了一种能够同一时候有多个线程进入临界区的同步器,它维护了一个状态表示可用的票 ... 
- 聊聊高并发(二十九)解析java.util.concurrent各个组件(十一) 再看看ReentrantReadWriteLock可重入读-写锁
		上一篇聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁 讲了可重入读写锁的基本情况和基本的方法,显示了怎样 ... 
- 谈论高并发(二十二)解决java.util.concurrent各种组件(四) 深入了解AQS(二)
		上一页介绍AQS其基本设计思路以及两个内部类Node和ConditionObject实现 聊聊高并发(二十一)解析java.util.concurrent各个组件(三) 深入理解AQS(一) 这篇说一 ... 
- 聊聊高并发(二十八)解析java.util.concurrent各个组件(十) 理解ReentrantReadWriteLock可重入读-写锁
		这篇讲讲ReentrantReadWriteLock可重入读写锁,它不仅是读写锁的实现,而且支持可重入性. 聊聊高并发(十五)实现一个简单的读-写锁(共享-排他锁) 这篇讲了怎样模拟一个读写锁. 可重 ... 
- 谈论高并发(三十)解析java.util.concurrent各种组件(十二) 认识CyclicBarrier栅栏
		这次谈话CyclicBarrier栅栏,如可以从它的名字可以看出,它是可重复使用. 它的功能和CountDownLatch类别似,也让一组线程等待,然后开始往下跑起来.但也有在两者之间有一些差别 1. ... 
- 高并发编程基础(java.util.concurrent包常见类基础)
		JDK5中添加了新的java.util.concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的访问都串行化了,这样保证了线程的安全性,所以这种方法 ... 
- 聊聊高并发(二十四)解析java.util.concurrent各个组件(六) 深入理解AQS(四)
		近期总体过了下AQS的结构.也在网上看了一些讲AQS的文章,大部分的文章都是泛泛而谈.又一次看了下AQS的代码,把一些新的要点拿出来说一说. AQS是一个管程.提供了一个主要的同步器的能力,包括了一个 ... 
- 聊聊高并发(四十四)解析java.util.concurrent各个组件(二十) Executors工厂类
		Executor框架为了更方便使用,提供了Executors这个工厂类.通过一系列的静态工厂方法.能够高速地创建对应的Executor实例. 仅仅有一个nThreads參数的newFixedThrea ... 
- 聊聊高并发(三十八)解析java.util.concurrent各个组件(十四) 理解Executor接口的设计
		JUC包中除了一系列的同步类之外,就是Executor运行框架相关的类.对于一个运行框架来说,能够分为两部分 1. 任务的提交 2. 任务的运行. 这是一个生产者消费者模式,提交任务的操作是生产者,运 ... 
随机推荐
- 【BZOJ 4380】4380: [POI2015]Myjnie (区间DP)
			4380: [POI2015]Myjnie Description 有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i].有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗 ... 
- 2017-2018-1 JAVA实验站 第二周作业
			2017-2018-1 JAVA实验站 第二周作业 小组成员: 组长 20162318张泰毓 成员 20162303石亚鑫 20162304张浩林 20162307张韵琪 20162321王彪 201 ... 
- fir.im Weekly - 嘘,关于***!
			上 Github 交友刷 StackOverflow 解惑,是攻城狮必备技能,加快打怪练级速度.关于,@左耳朵耗子 在微博上分享了一篇文档,轻一点教你建一个VPN服务器,重一点到教你在路由器上***, ... 
- 【8.22校内测试】【数学】【并查集】【string】
			今天的t2t3能打出来80分的暴力都好满足啊QwQ.(%%%$idy$ 今天的签到题,做的时候一眼就看出性质叻qwq.大于11的所有数分解合数都可以用4.6.9表示,乱搞搞就可以了. #include ... 
- 2015 UESTC 搜索专题N题 韩爷的梦 hash
			韩爷的梦 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/contest/show/61 Descrip ... 
- 安卓之service简单介绍
			一 什么是Service 二 如何使用Service 三 Service的生命周期 一 什么是Service Service,看名字就知道跟正常理解的“服务”差不多,后台运行,可交互这样的一个东西 ... 
- 复制到剪切板js代码(转)
			<script type="text/javascript" language="javascript"> //复制到剪切板js代码 functio ... 
- STN1110 Multiprotocol OBD to UART Interpreter
			http://www.obdsol.com/stn1110/ Safe, secure bootloader. Reflash the firmware in the field, even over ... 
- Visual Studio技巧集锦
			总结了一下VS的使用快捷键, 以下这些是必须转化为肌肉记忆的. 1.Ctrl+Shift+V循环粘贴不同的内容 剪贴板最多可以保存20项内容,通过Ctrl+Shift+V可以循环粘贴出之前复制过的内容 ... 
- JAVA包管理
			package cn.java.mybole; class test6 { public static void main(String[] args) { System.out.println(&q ... 
