在分析原子类之前,先来了解CAS操作

CAS

  CAS,compare and swap的缩写,中文翻译成比较并交换。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。

无论哪种情况,它都会在 CAS 指令之前返回该 位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前 值。)

CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”

  通常将 CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新 值 B,然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功。

  java中提供了对CAS操作的支持,具体在sun.misc.Unsafe类中,声明如下:

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

  上面三个方法都是类似的,主要对4个参数做一下说明。

var1:表示要操作的对象

var2:表示要操作对象中属性地址的偏移量

var4:表示需要修改数据的期望的值

var5:表示需要修改为的新值

  JUC包中大部分功能都是依靠CAS操作完成的,所以这块也是非常重要的.

synchronizedReentrantLock这种独占锁属于悲观锁,它是在假设需要操作的代码一定会发生冲突的,执行代码的时候先对代码加锁,让其他线程在外面等候排队获取锁。

悲观锁如果锁的时间比较长,会导致其他线程一直处于等待状态,像我们部署的web应用,一般部署在tomcat中,内部通过线程池来处理用户的请求,如果很多请求都处于等待获取锁的状态,可能会耗尽tomcat线程池,7

从而导致系统无法处理后面的请求,导致服务器处于不可用状态。

  除此之外,还有乐观锁,乐观锁的含义就是假设系统没有发生并发冲突,先按无锁方式执行业务,到最后了检查执行业务期间是否有并发导致数据被修改了,如果有并发导致数据被修改了 ,就快速返回失败,

  这样的操作使系统并发性能更高一些。cas中就使用了这样的操作。

CAS 的问题

ABA问题

CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了

  这就是CAS的ABA问题。常见的解决思路使用版本号

  在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。目前在JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。

循环时间长开销大

  上面我们说过如果CAS不成功,则会原地循环(自旋操作),如果长时间自旋会给CPU带来非常大的执行开销。并发量比较大的情况下,CAS成功概率可能比较低,可能会重试很多次才会成功。


JUC中原子类介绍

  什么是原子操作?

    atomic 翻译成中文是原子的意思。在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的。

    在我们这里 atomic 是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰,所以,所谓原子类说简单点就是具有原子操作特征的类,

    原子操作类提供了一些修改数据的方法,这些方法都是原子操作的,在多线程情况下可以确保被修改数据的正确性

JUC中对原子操作提供了强大的支持,这些类位于java.util.concurrent.atomic包中,如下图:

          

JUC中原子类思维导图

基本类型原子类

使用原子的方式更新基本类型

  • AtomicInteger:int类型原子类
  • AtomicLong:long类型原子类
  • AtomicBoolean :boolean类型原子类

这几个类的用法基本一致,这里以AtomicInteger为例总结常用的方法 

  1. public final int get() //获取当前的值
  2. public final int getAndSet(int newValue)//获取当前的值,并设置新的值
  3. public final int getAndIncrement()//获取当前的值,并自增
  4. public final int getAndDecrement() //获取当前的值,并自减
  5. public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
  6. boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
  7. public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。

数组类型原子类介绍

使用原子的方式更新数组里的某个元素,可以确保修改数组中数据的线程安全性。

  • AtomicIntegerArray:整形数组原子操作类
  • AtomicLongArray:长整形数组原子操作类
  • AtomicReferenceArray :引用类型数组原子操作类

这几个类的用法一致,就以AtomicIntegerArray来总结下常用的方法:

  1. public final int get(int i) //获取 index=i 位置元素的值
  2. public final int getAndSet(int i, int newValue)//返回 index=i 位置的当前的值,并将其设置为新值:newValue
  3. public final int getAndIncrement(int i)//获取 index=i 位置元素的值,并让该位置的元素自增
  4. public final int getAndDecrement(int i) //获取 index=i 位置元素的值,并让该位置的元素自减
  5. public final int getAndAdd(int delta) //获取 index=i 位置元素的值,并加上预期的值
  6. boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将 index=i 位置的元素值设置为输入值(update)
  7. public final void lazySet(int i, int newValue)//最终 将index=i 位置的元素设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。

引用类型原子类介绍

基本类型原子类只能更新一个变量,如果需要原子更新多个变量,需要使用 引用类型原子类。

  • AtomicReference:引用类型原子类
  • AtomicStampedRerence:原子更新引用类型里的字段原子类
  • AtomicMarkableReference :原子更新带有标记位的引用类型

对象的属性修改原子类介绍

如果需要原子更新某个类里的某个字段时,需要用到对象的属性修改原子类。

  • AtomicIntegerFieldUpdater:原子更新整形字段的值
  • AtomicLongFieldUpdater:原子更新长整形字段的值
  • AtomicReferenceFieldUpdater :原子更新应用类型字段的值
  • AtomicStampedFieldUpdater: 原子更新带有版本号的引用类型。

使用AtomicStampedRerence解决ABA的问题

它内部不仅维护了对象的值,还维护了一个时间戳(我们这里把他称为时间戳,实际上它可以使用任何一个整形来表示状态值),当AtomicStampedRerence对应的数值被修改时,除了更新数据本身外,还必须要更新时间戳。

当AtomicStampedRerence设置对象值时,对象值及时间戳都必须满足期望值,写入才会成功。因此,即使对象值被反复读写,写回原值,只要时间戳发生变量,就能防止不恰当的写入。

高性能原子类

  高性能原子类,是java8中增加的原子类,它们使用分段的思想,把不同的线程hash到不同的段上去更新,最后再把这些段的值相加得到最终的值,这些类主要有:

  (1)Striped64:下面四个类的父类。

  (2)LongAccumulator:long类型的聚合器,需要传入一个long类型的二元操作,可以用来计算各种聚合操作,包括加乘等。

  (3)LongAdder:long类型的累加器,LongAccumulator的特例,只能用来计算加法,且从0开始计算。

  (4)DoubleAccumulator:double类型的聚合器,需要传入一个double类型的二元操作,可以用来计算各种聚合操作,包括加乘等。

  (5)DoubleAdder:double类型的累加器,DoubleAccumulator的特例,只能用来计算加法,且从0开始计算。

感谢:https://mp.weixin.qq.com/s?__biz=MzA5MTkxMDQ4MQ==&mid=2648933166&idx=1&sn=15e614500676170b76a329efd3255c12&chksm=88621b10bf1592064befc5c9f0d78c56cda25c6d003e1711b85e5bfeb56c9fd30d892178db87&scene=21#wechat_redirect

https://www.jianshu.com/p/424966dc7ab2

JUC之原子类的更多相关文章

  1. JUC包-原子类(AtomicInteger为例)

    目录 JUC包-原子类 为什么需要JUC包中的原子类 原子类原理(AtomicInteger为例) volatile CAS CAS的缺点 ABA问题 什么是ABA问题 ABA问题的解决办法 JUC包 ...

  2. java多线程系类:JUC原子类:03之AtomicLongArray原子类

    概要 AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray这3个数组类型的原子类的原理和用法相似.本章以AtomicLongArray对数 ...

  3. Java多线程系列--“JUC原子类”02之 AtomicLong原子类

    概要 AtomicInteger, AtomicLong和AtomicBoolean这3个基本类型的原子类的原理和用法相似.本章以AtomicLong对基本类型的原子类进行介绍.内容包括:Atomic ...

  4. Java多线程系列--“JUC原子类”03之 AtomicLongArray原子类

    概要 AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray这3个数组类型的原子类的原理和用法相似.本章以AtomicLongArray对数 ...

  5. Java多线程系列--“JUC原子类”04之 AtomicReference原子类

    概要 本章对AtomicReference引用类型的原子类进行介绍.内容包括:AtomicReference介绍和函数列表AtomicReference源码分析(基于JDK1.7.0_40)Atomi ...

  6. Java多线程系列--“JUC原子类”05之 AtomicLongFieldUpdater原子类

    概要 AtomicIntegerFieldUpdater, AtomicLongFieldUpdater和AtomicReferenceFieldUpdater这3个修改类的成员的原子类型的原理和用法 ...

  7. JUC——原子类操作(三)

    原子类操作 既然强调了并发访问,那么就必须考虑操作系统位数:32位操作系统还是64位操作系统,对于long型数据类型而言,是64位的.但是如果现在项目运行在32位系统上,则long型数据会占用32位空 ...

  8. Java多线程—JUC原子类

    根据修改的数据类型,可以将JUC包中的原子操作类可以分为4类. 1. 基本类型: AtomicInteger, AtomicLong, AtomicBoolean ;2. 数组类型: AtomicIn ...

  9. java 多线程系列---JUC原子类(三)之AtomicLongArray原子类

    AtomicLongArray介绍和函数列表 在"Java多线程系列--“JUC原子类”02之 AtomicLong原子类"中介绍过,AtomicLong是作用是对长整形进行原子操 ...

随机推荐

  1. 史上最全java pdf精品书籍整理

    算法,多线程,spring,数据库,大数据,面试题等等.喜欢的小伙伴加群获取 QQ群号825199617 (非广告培训技术群,纯java知识交流,请自重)

  2. Intellij IDEA 从入门到上瘾 图文教程

    1. IDEA VS Eclipse 核心术语比较 ​ 由下图可见:两者最大的转变就在于工作空间概念的转变,并且在IDEA当中,Project和 Module是作为两个不同的概念,对项目结构是具有重大 ...

  3. 两个对象key相同但是value不同,将value不同的键值对以对象形式输出

    let obj={ name:'jack', age:18, sex:'girl' } let obj2={ name:'rose', age:18, sex:'boy' } var str={} f ...

  4. mysql连接数

    如何实时查看mysql当前连接数? 如何实时查看mysql当前连接数? 1.查看当前所有连接的详细资料: ./mysqladmin -uadmin -p -h10.140.1.1 processlis ...

  5. SOA面向服务体系架构

    SOA概念 1.什么是SOA 面向服务的体系结构(Service-Oriented Architecture,SOA)是一个组件模型. 它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的 ...

  6. 文献阅读 | The single-cell transcriptional landscape of mammalian organogenesis | 器官形成 | 单细胞转录组

    The single-cell transcriptional landscape of mammalian organogenesis 老板已经提了无数遍的文章,确实很nb,这个工作是之前我们无法想 ...

  7. 用variant的数据来推导基因表达 | Imputation of Expression Using PrediXcan

    一个工具的逻辑得足够完善.意义足够重大,才有资格发在NG上. A gene-based association method for mapping traits using reference tr ...

  8. linux下的/dev/shm/及对Oracle 的影响

    一./dev/shm/介绍: /dev/shm/是linux下一个非常有用的目录,因为这个目录不在硬盘上,而是在内存里.因此在linux下,就不需要大费周折去建ramdisk,直接使用/dev/shm ...

  9. 宝塔php open_basedir restriction in effect

    解决方法一: 1.网站管理的  防跨站攻击去掉勾选,重启网站,清除浏览器缓存 解决方法二:

  10. Android: ListView与Button的共存问题解决

    ListView 和 其它能触发点击事件的widget无法一起正常工作的原因是加入其它widget后,ListView的itemclick事件将无法触发,被其它widget的click事件屏蔽.   ...