java.util.concurrent.atomic原子操作类包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。可以对基本数据、数组中的基本数据、对类中的基本数据进行操作。原子变量类相当于一种泛化的volatile变量,能够支持原子的和有条件的读-改-写操作。

java.util.concurrent.atomic中的类可以分成4组:

标量类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
更新器类:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
复合变量类:AtomicMarkableReference,AtomicStampedReference

一、标量类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
  AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference这四种基本类型用来处理布尔,整数,长整数,对象四种数据,其内部实现不是简单的使用synchronized,而是一个更为高效的方式CAS (compare and swap) + volatile和native方法,从而避免了synchronized的高开销,执行效率大为提升。其实例各自提供对相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。
以AtomicInteger的源码为例来进行学习:

public class AtomicInteger extends Number implements java.io.Serializable {// ……private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;//……private volatile int value;public AtomicInteger(int initialValue) {value = initialValue;}public AtomicInteger() {}public final int get() {return value;}public final void set(int newValue) {value = newValue;}public final void lazySet(int newValue) {unsafe.putOrderedInt(this, valueOffset, newValue);}public final int getAndSet(int newValue) {for (;;) {int current = get();if (compareAndSet(current, newValue))return current;}}public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}public final boolean weakCompareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}
}

1、set()和get()方法可以原子的设定和获取atomic的数据,类似于volatile,保证数据会在主存中设置或读取。
2、void set()和void lazySet():set设置为给定值,直接修改原始值;lazySet延时设置变量值,这个等价于set()方法,但是由于字段是volatile类型的,因此次字段的修改会比普通字段(非volatile字段)有稍微的性能延时(尽管可以忽略),所以如果不是想立即读取设置的新值,允许在“后台”修改值,那么此方法就很有用。
3、getAndSet()方法
原子的将变量设定为新数据,同时返回先前的旧数据。其本质是get()操作,然后做set()操作。尽管这2个操作都是atomic,但是他们合并在一起的时候,就不是atomic。在Java的源程序的级别上,如果不依赖synchronized的机制来完成这个工作,是不可能的。只有依靠native方法才可以。
4、compareAndSet()和weakCompareAndSet()
这两个方法都是conditional modifier方法。这2个方法接受2个参数,一个是期望数据(expected),一个是新数据(new);如果atomic里面的数据和期望数据一 致,则将新数据设定给atomic的数据,返回true,表明成功;否则就不设定,并返回false。JDK规范中说:以原子方式读取和有条件地写入变量但不 创建任何 happen-before 排序,因此不提供与除 weakCompareAndSet 目标外任何变量以前或后续读取或写入操作有关的任何保证。大意就是说调用weakCompareAndSet时并不能保证不存在happen- before的发生(也就是可能存在指令重排序导致此操作失败)。但是从Java源码来看,其实此方法并没有实现JDK规范的要求,最后效果和 compareAndSet是等效的,都调用了unsafe.compareAndSwapInt()完成操作。

虽然原子的标量类扩展了基本类型的类,但是并没有扩展基本类型的包装类,如Integer或Long,事实上它们也不能直接扩展。因为基本类型的包装类是不可以修改的,而原子变量类是可以修改的。在原子变量类中没有重新定义hashCode或equals方法,每个实例都是不同的,他们也不宜用做基于散列容器中的键值。

二、数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
  AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray类进一步扩展了原子操作,对这些类型的数组提供了支持。这些类在为其数组元素提供volatile访问语义方面也引人注目,这对于普通数组来说是不受支持的。其内部并不是像AtomicInteger一样维持一个volatile变量,而是全部由native方法实现。数组变量进行volatile没有意义,因此set/get就需要unsafe来做了,但是多了一个index来指定操作数组中的哪一个元素。

三、更新器类:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
 AtomicReferenceFieldUpdater,AtomicIntegerFieldUpdater和AtomicLongFieldUpdater 是基于反射的实用工具,可以提供对关联字段类型的访问,可用于获取任意选定volatile字段上的compareAndSet操作。它们主要用于原子数据结构中,该结构中同一节点的几个 volatile 字段都独立受原子更新控制。这些类在如何以及何时使用原子更新方面具有更大的灵活性,但相应的弊端是基于映射的设置较为拙笨、使用不太方便,而且在保证方面也较差。
使用中要注意一下几点:

(1)字段必须是volatile类型的
(2)字段的描述类型(修饰符public/protected/default/private)是与调用者与操作对象字段的关系一致。也就是说 调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。
(3)只能是实例变量,不能是类变量,也就是说不能加static关键字。
(4)只能是可修改变量,不能使final变量,因为final的语义就是不可修改。实际上final的语义和volatile是有冲突的,这两个关键字不能同时存在。
(5)对于AtomicIntegerFieldUpdater 和AtomicLongFieldUpdater 只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater 。

netty5.0中类ChannelOutboundBuffer统计发送的字节总数,由于使用volatile变量已经不能满足,所以使用AtomicIntegerFieldUpdater 来实现的,看下面代码:

//定义
private static final AtomicLongFieldUpdater<ChannelOutboundBuffer> TOTAL_PENDING_SIZE_UPDATER =AtomicLongFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "totalPendingSize");private volatile long totalPendingSize;//使用
long oldValue = totalPendingSize;
long newWriteBufferSize = oldValue + size;
while (!TOTAL_PENDING_SIZE_UPDATER.compareAndSet(this, oldValue, newWriteBufferSize)) {oldValue = totalPendingSize;newWriteBufferSize = oldValue + size;
}

四、复合变量类:AtomicMarkableReference,AtomicStampedReference
  AtomicMarkableReference 类将单个布尔值与引用关联起来。维护带有标记位的对象引用,可以原子方式更新带有标记位的引用类型。
  AtomicStampedReference 类将整数值与引用关联起来。维护带有整数“标志”的对象引用,可以原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和版本号,可以解决使用CAS进行原子更新时,可能出现的ABA问题。

附录:java并发编程中的CAS和ABA问题
  CAS(compare and swap)比较和替换是java5+提供的设计并发算法时用到的一种技术。是使用一个期望值和一个变量的当前值进行比较,如果当前值和我们的期望值相等,就使用一个新值替换掉当前值。
CAS适用场景:
我们来看下面一段代码

class MyLock {private boolean locked = false;public boolean lock() {if(!locked) {locked = true;return true;}return false;}
}

熟悉多线程的同学可以很明显的看出上面的代码在多线程环境中会出现问题。
  为了避免在多线程的环境中的错误我们可以将locked的检查和更改放在一个原子代码块中执行,因为不存在多线程同时执行原子代码块的问题。
我们可以做如下更改:

class MyLock {private boolean locked = false;public synchronized boolean lock() {if(!locked) {locked = true;return true;}return false;}
}

以上方法使用了synchronized关键字,那么使用CAS可以如何操作呢?

public static classMyLock{private AtomicBoolean locked = new AtomicBoolean(false);public boolean lock(){return locked.compareAndSet(false,true);}
}

此时locked变量不再是boolean类型卫视AtomicBoolean,使用了AtomicBoolean的compareAndSet()方法,用一个期望值和AtomicBoolean实例的值比较,若两者相等,则使用一个新值替换原来的值。
  但是在使用CAS实现原子操作可能带来ABA问题.
  我们知道CAS的原理是在比较操作值的时候,检查值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,中间变成B,最后又变成A,那么使用CAS进行检查时会发现值没有发生变化,但是实际上是变化了的。
  这个时候该怎么办呢?这个问题乍一看跟版本冲突的问题类似,所以,我们可以使用添加版本号的方法来解决。
  JDK1.5之后,Atomic包提供的类AtomicStampedReference就解决了这个问题。这个类的compareAndSet方法先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。源码如下:

public boolean weakCompareAndSet(V   expectedReference,V   newReference,int expectedStamp,int newStamp) {return compareAndSet(expectedReference, newReference,expectedStamp, newStamp);
}

【JDK源码】java.util.concurrent.atomic包常用类详解相关推荐

  1. 深入Synchronized和java.util.concurrent.locks.Lock的区别详解

    转载自  深入Synchronized和java.util.concurrent.locks.Lock的区别详解 本篇文章是对Synchronized和java.util.concurrent.loc ...

  2. 【JDK源码】java.io包常用类详解

    看完java.io的JDK源码,在网上发现一篇关于java.io中的类使用的文章总结的很全面,看完之后在原文的基础上加了一些自己的总结如下构成了本篇文章.原文地址 一.Java Io流 1. Java ...

  3. 【JDK源码】java.lang包常用类详解

    接下来的几天开始JDK源码的学习和总结,之前看<java编程思想>的时候看到java的基础知识有很多,其中支撑着这些基础的基础中的基础当属JDK.JDK的基础代码里面又分了很多基础的模块, ...

  4. java.util.concurrent.atomic原子操作类包

    2019独角兽企业重金招聘Python工程师标准>>> 这个包里面提供了一组原子变量类.其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当 ...

  5. java并发编程3:使用JDK并发包(java.util.concurrent)构建程序

    原文地址为: java并发编程3:使用JDK并发包(java.util.concurrent)构建程序 java.util.concurrent 概述 JDK5.0 以后的版本都引入了高级并发特性,大 ...

  6. 【问题已解决】Unrecognized option: --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED

    今天在创建java项目时,运行报错,说无法成功创建java程序. Unrecognized option: --add-opens=java.base/java.util.concurrent.ato ...

  7. KBQA_多轮对话——模型源码解析(一)Pickle模块功能详解

    KBQA_多轮对话--模型源码解析(一)Pickle模块功能详解 pickle --- Python 对象序列化的基本功能 1.pickle基本概念 2.pickle 与 json 模块的比较 3.p ...

  8. 【SA8295P 源码分析】53 - mifs.build.tmpl 脚本详解:启动QNX procnto-smp-instr微内核、启动QNX串口终端shell、加载解析并执行ifs2_la.img

    [SA8295P 源码分析]53 - mifs.build.tmpl 脚本详解:启动QNX procnto-smp-instr微内核.启动QNX串口终端shell.加载解析并执行ifs2_la.img ...

  9. [Jdk源码学习]聊聊concurrent包下面的volite*

    引子:关于Java并发中的volatile关键字 并切--定义 悲观锁: 1.一个线程在执行一个操作时持有对一个资源的独占锁(A线程占了资源a,则其他线程就不能操作资源a) 2.一般用在冲突比较可能发 ...

最新文章

  1. 电脑怎么连蓝牙耳机_蓝牙耳机怎么关机
  2. 网络学习:VLAN和独臂路由
  3. 局部特征(5)——如何利用彩色信息 Color Descriptors
  4. asp.net 2.0下嵌套masterpage页的可视化编辑
  5. 数论--费马小定理求逆元
  6. 如何在 Entity Framework 中计算 时间差 ?
  7. 牛客小白月赛11 Rinne Loves Xor
  8. 【Python】Matplotlib绘制正余弦曲面图
  9. Oracle Bitmap 索引结构、如何存储及其优势
  10. 智数合一,智慧工厂的四大典型应用场景
  11. 1. SOAP 简介
  12. FireFox支持NPAPI接口,最新版本是52.9,53已经不支持了
  13. 3d激光雷达开发(点云数据显示)
  14. Keil5中添加C51芯片
  15. 【java】BeanUtils.populate()的使用
  16. Java程序监控工具
  17. python 标准差计算(std)
  18. txt、csv、trc、log格式转换成asc
  19. elementUi el-dialog 对话框实现可拖拽、去掉覆盖层、并可操作底层的按钮
  20. 在Linux中运行Nancy应用程序

热门文章

  1. iOS 7 iPhone iPad应用开发技术详解
  2. web2.0网站的配色参考方案
  3. 解决一条高难度的,关于时间段 数据汇总问题
  4. Eclipse中代码编辑背景颜色修改和XML字体修改
  5. JavaScript instanceof的实现
  6. git 简易指南+常用命令
  7. 持续提高 Android 应用的安全性与性能
  8. Android自定义组合控件--EditText和Button组合成带有清空EditText内容功能的复合控件
  9. Dlib学习笔记:dlib array2d与 OpenCV Mat互转
  10. 以太坊源码分析——BlockChain