一、引言

在 JDK1.5 中新增 java.util.concurrent(J.U.C) 包,它建立在 CAS 之上。CAS 是非阻塞算法的一种常见实现,相对于 synchronized 这种阻塞算法,它的性能更好。

DK1.5 中引入了底层的支持,在 int、long 和对象的引用等类型上都公开了 CAS 的操作,并且 JVM 把它们编译为底层硬件提供的最有效的方法,在运行 CAS 的平台上,运行时把它们编译为相应的机器指令。在 java.util.concurrent.atomic 包下面的所有的原子变量类型中,比如 AtomicInteger,都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的 CAS 操作。

二、Java原子类介绍

​atomic 包中的 13 个类,属于 4 种类型的原子更新方式.

  • (1)原子更新基本类型

  • (2)原子更新数组

  • (3)原子更新引用

  • (4)原子更新属性

atomic 包里的类基本都是使用 Unsafe 实现的包装类.

1.原子更新基本类型

  • AtomicBoolean:原子更新布尔类型。
  • AtomicInteger:原子更新整型。
  • AtomicLong:原子更新长整型。

本类以 AtomicInteger 进行讲解:

AtomicInteger 是一个支持原子操作的 Integer 类,就是保证对 AtomicInteger 类型变量的增加和减少操作是原子性的,不会出现多个线程下的数据不一致问题。如果不使用 AtomicInteger,要实现一个按顺序获取的 ID,就必须在每次获取时进行加锁操作,以避免出现并发时获取到同样的 ID 的现象。

  • int addAndGet(int delta) 以原子方式将输入的数值与实例中的值相加,并返回结果;
  • boolean compareAndSet(int expect, int update) 如果输入的数值等于预期值,则以原子的方式将该值设置为输入的值;
  • int getAndIncrement() 以原子方式将当前值加 1,注意,这里返回的是自增前的值;
  • void lazySet(int newValue) 最终会设置成 newValue,使用 lazySet 设置值后,可能导致其他线程在之后的一小段时间内还是可以读到 旧值;
  • int getAndSet(int newValue) 以原子方式设置为 newValue 的值,并返回旧值。

那么getAndIncrement 是如何实现原子操作的呢?

public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; }
}

我们可以看到,它的实现原理是死循环 + CAS。

2.原子更新数组

通过原子的方式更新数组里的某个元素,Atomic 包提供了以下 3 个类。

  • AtomicIntegerArray:原子更新整型数组里的元素。
  • AtomicLongArray:原子更新长整型数组里的元素。
  • AtomicReferenceArray:原子更新引用类型数组里的元素。

AtomicIntegerArray 类提供方法如下:

  • int addAndGet(int i, int delta) 以原子方式将输入值与数组中的索引 i 的元素相加
  • boolean compareAndSet(int i, int expect, int update) 如果当前值等于预期值,则以原子方式将数组位置 i 的元素设置成 update 值.

3.原子更新引用类型

原子更新基本类型的 AtomicInteger,只能更新一个变量,如果要原子更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic 包提供了以下 3 个类。

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

4.原子更新属性

如果需原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic 包提供了以下 3 个类进行原子字段更新。

  • AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
  • AtomicLongFieldUpdater:原子更新长整型字段的更新器。
  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。

三、原子类的应用场景

原子变量不需要使用锁或其他同步机制来保护对其值的并发访问。所有操作都是基于CAS原子操作的。他保证了多线程在同一时间操作一个原子变量而不会产生数据不一致的错误,并且他的性能优于使用同步机制保护的普通变量,譬如说在多线程环境中统计次数就可以使用原子变量。具体场景总结如下:

  1. 在多线程环境中,可以很多情况可使用该关键字替换synchronized;
  2. 限制并发流量:在高并发的场景下,也可以用来限制接口的流量,超过并发的数量的阈值进行熔断等操作;
  3. AtomicBoolean解决了初始化中多线程环境下的并发安全问题。

四、总结

  1. 借助volatile原语,保证线程间的数据是可见的(共享的)。
  2. 2、采用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成 功为止 compareAndSet利用JNI来完成CPU指令的操作。
  3. ABA问题:比如说线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。如果链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。要解决"ABA问题",我们需要增加一个版本号,在更新变量值的时候不应该只更新一个变量值,而应该更新两个值,分别是变量值和版本号
  4. synchronized的成本相对较高,需要获取锁对象,释放锁对象  使用原子变量可以避免多线程的优先级倒置和死锁情况的发生,提升在高并发处理下的性能。

我的微信公众号:架构真经(id:gentoo666),分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。每日更新哦!

Java中的13个原子操作类介绍相关推荐

  1. Java中的13个原子操作类

    点击上方"Java中文社群",选择"设为星标" 终身学习者,以编程为乐! 来源:https://www.iteye.com/blog/xiaoheng-2509 ...

  2. Java中工程、包、类介绍

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/FX677588/article/det ...

  3. Java Review - 并发编程_原子操作类LongAdder LongAccumulator剖析

    文章目录 概述 小Demo 源码分析 重要的方法 long sum() reset sumThenReset longValue() add(long x) longAccumulate(long x ...

  4. java 为什么有匿名类_全面了解Java中的内部类和匿名类

    Java内部类(Inner Class),类似的概念在C++里也有,那就是嵌套类(Nested Class),乍看上去内部类似乎有些多余,它的用处对于初学者来说可能并不是那么显著,但是随着对它的深入了 ...

  5. java 实体属性个数_?Java中比较实用实体转换工具介绍

    ​Java中比较实用实体转换工具介绍 文中源码地址 大家一般编码过程中,经常会遇到DO对象转化为DTO对象,对象和对象之间转换一般需要用到转换工具,毕竟使用getter/setter太过麻烦 DO:D ...

  6. JAVA中JVM的重排序详细介绍(写得很明白)

    刚刚在研究volatile变量的时候,涉及到重排序的概念,于是发现了这篇很好的文章,写得很简短很明白.所以转载一下. 原文地址:JAVA中JVM的重排序详细介绍 原文贴出来: 重排序通常是编译器或运行 ...

  7. Java 中的线程安全的类

    Java 中的线程安全的类 3 个线程安全的类 它们对应的非线程安全的类 它们延伸的类 其它 它们对应的非线程安全的类 3 个线程安全的类   Java 中的 3 个基本的线程安全的类为:Hashta ...

  8. java中常用的包、类、以及包中常用的类、方法、属性----sql和text\swing

    java中常用的包.类.以及包中常用的类.方法.属性 常用的包 java.io.*; java.util.*; java.lang.*; java.sql.*; java.text.*; java.a ...

  9. java面试题27 java中下面哪些是Object类的方法()

    java面试题27 java中下面哪些是Object类的方法() A notify() B notifyAll() C sleep() D wait() 蒙蔽树上蒙蔽果,蒙蔽树下你和我.遇到这种题,我 ...

最新文章

  1. 存储过程里output的使用总结
  2. 深度学习-最优化笔记
  3. Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (111)解决方法
  4. SAP收发存报表程序
  5. 洛谷3613睡觉困难综合征(LCT维护链信息(前后缀)+贪心)
  6. 38 FI配置-财务会计-固定资产-组织结构-指定帐户确定
  7. 如何使用JavaScript或JQuery检测一个URL文件是否存在?
  8. swift 2.0 语法 循环
  9. js知识学习图谱,新手必看
  10. c#项目遇到的问题及解决(1)
  11. ios12后,获取不到WIFI(SSID)相关信息
  12. [C++]2-3 倒三角形
  13. 图片随着屏幕大小变化而变化
  14. 如果牛顿是程序员,那么?
  15. ubuntu的pycharm或程序卡主,强制关闭方法
  16. xp驱动和Win7驱动的区别
  17. 毕业设计之 - 基于深度学的图像修复 图像补全
  18. Java常用设计模式(三)
  19. 初学verilog必看
  20. 码云果园:术后人群怎么吃水果

热门文章

  1. 统计带头结点的单向链表的个数并存放在形参n所指的单元中。 欢迎评论 指点。
  2. Docker 深入理解概念
  3. golang 切片追加问题
  4. PDOStatement::bindParam的一个陷阱
  5. 数据结构与算法--递归(Recursion Algorithm)
  6. 大数据学习知识点导图
  7. 用Kubernetes搭建便携式开发环境之MongoDB
  8. Golang程序性能分析(一)pprof和go-torch
  9. 老有人问你什么是Docker?把这篇干货文章甩给他!
  10. OSX EI Captain中安装Pear等三方软件不成功的解决方法