AtomicInteger、AtomicBoolean、AtomicLong、AtomicReference 这些原子类型,它们无一例外都采用了基于 volatile 关键字 +CAS 算法无锁的操作方式来确保共享数据在多线程操作下的线程安全性。

  • volatile关键字保证了线程间的可见性,当某线程操作了被volatile关键字修饰的变量,其他线程可以立即看到该共享变量的变化。
  • CAS算法,即对比交换算法,是由UNSAFE提供的,实质上是通过操作CPU指令来得到保证的。CAS算法提供了一种快速失败的方式,当某线程修改已经被改变的数据时会快速失败。
  • 当CAS算法对共享数据操作失败时,因为有自旋算法的加持,我们对共享数据的更新终究会得到计算。

总之,原子类型用自旋+CAS的无锁操作保证了共享变量的线程安全性和原子性。

绝大多数情况下,CAS算法并没有什么问题,但是在需要关心变化值的操作中会存在 ABA 的问题,比如一个值原来是A,变成了B,后来又变成了A,那么CAS检查时会发现它的值没有发生变化,但是实际上却是发生了变化的。

如何避免CAS算法带来的ABA问题呢?针对乐观锁在并发情况下的操作,我们通常会增加版本号,比如数据库中关于乐观锁的实现方式,以此来解决并发操作带来的ABA问题。在Java原子包中也提供了这样的实现AtomicStampedReference<E>。

1、AtomicStampedReference 详解

AtomicStampedReference在构建的时候需要一个类似于版本号的int类型变量stamped,每一次针对共享数据的变化都会导致该 stamped 的变化(stamped 需要应用程序自身去负责,AtomicStampedReference并不提供,一般使用时间戳作为版本号),因此就可以避免ABA问题的出现,AtomicStampedReference的使用也是极其简单的,创建时我们不仅需要指定初始值,还需要设定stamped的初始值,在AtomicStampedReference的内部会将这两个变量封装成Pair对象,代码如下所示。

 静态内部类,封装了 变量引用 和 版本号
private static class Pair<T> {final T reference;  // 变量引用final int stamp;    // 版本号private Pair(T reference, int stamp) {this.reference = reference;this.stamp = stamp;}static <T> Pair<T> of(T reference, int stamp) {return new Pair<T>(reference, stamp);}}private volatile Pair<V> pair;/**** @param initialRef  初始变量引用* @param initialStamp  版本号*/public AtomicStampedReference(V initialRef, int initialStamp) {pair = Pair.of(initialRef, initialStamp);}

2、常用方法

// 构造函数,初始化引用和版本号
public AtomicStampedReference(V initialRef, int initialStamp)// 以原子方式获取当前引用值
public V getReference()// 以原子方式获取当前版本号
public int getStamp()// 以原子方式获取当前引用值和版本号
public V get(int[] stampHolder)// 以原子的方式同时更新引用值和版本号
// 当期望引用值不等于当前引用值时,操作失败,返回false
// 当期望版本号不等于当前版本号时,操作失败,返回false
// 在期望引用值和期望版本号同时等于当前值的前提下
// 当新的引用值和新的版本号同时等于当前值时,不更新,直接返回true
// 当新的引用值和新的版本号不同时等于当前值时,同时设置新的引用值和新的版本号,返回true
public boolean weakCompareAndSet(V  expectedReference,V  newReference,int expectedStamp,int newStamp)// 以原子的方式同时更新引用值和版本号
// 当期望引用值不等于当前引用值时,操作失败,返回false
// 当期望版本号不等于当前版本号时,操作失败,返回false
// 在期望引用值和期望版本号同时等于当前值的前提下
// 当新的引用值和新的版本号同时等于当前值时,不更新,直接返回true
// 当新的引用值和新的版本号不同时等于当前值时,同时设置新的引用值和新的版本号,返回true
public boolean compareAndSet(V   expectedReference,V   newReference,int expectedStamp,int newStamp)// 以原子方式设置引用的当前值为新值newReference
// 同时,以原子方式设置版本号的当前值为新值newStamp
// 新引用值和新版本号只要有一个跟当前值不一样,就进行更新
public void set(V newReference, int newStamp)// 以原子方式设置版本号为新的值
// 前提:引用值保持不变
// 当期望的引用值与当前引用值不相同时,操作失败,返回fasle
// 当期望的引用值与当前引用值相同时,操作成功,返回true
public boolean attemptStamp(V expectedReference, int newStamp)// 使用`sun.misc.Unsafe`类原子地交换两个对象
private boolean casPair(Pair<V> cmp, Pair<V> val)

并发编程 — AtomicStampedReference 详解相关推荐

  1. 1.1.3 J.U.C并发编程包详解

    目录 1.3.1 Lock接口及其实现 1.3.2 AQS抽象队列同步器详解 1.3.3 并发容器类-1 1.3.4 并发容器类-2 1.3.5 Fork/Join框架详解 1.3.1 Lock接口及 ...

  2. 它来了,阿里架构师的“Java多线程+并发编程”知识点详解手册,限时分享

    自学Java的时候,多线程和并发这一块可以说是最难掌握的部分了,很多小伙伴表示需要一些易于学习和上手的资料. 所以今天这份「Java并发学习手册」就是一份集中学习多线程和并发的手册,PDF版,由Red ...

  3. Java 并发编程_详解 synchronized 和 volatile

    文章目录 1. synchronized 的应用 1.1 基础知识 1.2 synchronized 语法 2. Monitor概念 3. Synchronized原理进阶 3.1 对象头格式 3.2 ...

  4. Java并发编程AQS详解

    本文内容及图片代码参考视频:https://www.bilibili.com/video/BV12K411G7Fg/?spm_id_from=333.788.recommend_more_video. ...

  5. Java并发编程synchronized详解

    一.关于临界区.临界资源.竞态条件和解决方法 首先看如下代码,thread1对变量i++做500次运算,thread2对i--做500次运算,但是最终的结果却可能为是正数,负数,0不一样的结果. pa ...

  6. JUC并发编程系列详解篇十四(自旋锁 VS 适应性自旋锁)

    自旋锁 由于在多处理器环境中某些资源的有限性,有时需要互斥访问(mutual exclusion),这时候就需要引入锁的概念,只有获取了锁的线程才能够对资源进行访问,由于多线程的核心是CPU的时间分片 ...

  7. Java并发编程——ConcurrentHashMap详解

    引出 场景:针对用户来做一个访问次数的记录. 通过HashMap进行记录,key为用户名,value为访问次数. public class ConcurrentHashMapDemo {private ...

  8. Java并发编程——ForkJoin详解

    概念 Fork/Join 框架是 Java7 提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架.类似于Java 8中的paralle ...

  9. 看完之后保证你对socket编程步骤胸有成竹。 C++ Socket网络编程基础详解(TCP)

    C++ Socket网络编程基础详解(TCP版) ​    网络编程,就是编写程序使得两台计算机交换数据,其实从本质上来讲,网络编程最终所实现的功能,和我们文件的输入输出很相似,只是文件输入输出的对象 ...

最新文章

  1. 概率论中指数分布介绍及C++11中std::exponential_distribution的使用
  2. python学习第四课
  3. python大学教程吕云翔课后题答案_软件工程实用教程吕云翔-课后答案.doc
  4. kafka消息存储格式
  5. Jquery操作对控件的取值、赋值
  6. Fragment 和 FragmentActivity的使用(二)
  7. 2019校招Android面试题解1.0
  8. qi接收启动协议_基于QI协议的无线充电通信系统
  9. java-画出二维码
  10. U盘不被电脑识别问题
  11. 商业贷款和公积金贷款差多少?一组数据告诉你!
  12. J2EE疑难解决实例--转自JSP中文网
  13. SSID, BSSID, ESSID
  14. Vijos1234 口袋的天空 题解
  15. 判断访问来源是pc端还是手机端
  16. 麒麟操作系统V10安装达梦数据库
  17. c# json 按照key顺序排序。。。
  18. Ubuntu 下安装 StarUML
  19. 壁虎书1 The Machine Learning Landscape
  20. 如何操作拆分Word文档?干货来啦!怎样上下拆分Word文档?

热门文章

  1. 为什么正态分布如此常见?
  2. AM335x启动流程(bootrom)
  3. 条码软件(Barcode Software)的类别、常用的条码软件、条码扫描软件经验分享
  4. VS2012及帮助(MSDN离线版)文档官网下载
  5. js中eq neq gt gte lt lte分别是什么意思
  6. 【HTTP协议】发展历程
  7. 倒计时H5页面源码(2018年除夕)
  8. django将返回json里的unicode转换为中文
  9. 一篇入门电容的选择和使用
  10. Pubmed格式字段说明