并发编程 — AtomicStampedReference 详解
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.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接口及 ...
- 它来了,阿里架构师的“Java多线程+并发编程”知识点详解手册,限时分享
自学Java的时候,多线程和并发这一块可以说是最难掌握的部分了,很多小伙伴表示需要一些易于学习和上手的资料. 所以今天这份「Java并发学习手册」就是一份集中学习多线程和并发的手册,PDF版,由Red ...
- Java 并发编程_详解 synchronized 和 volatile
文章目录 1. synchronized 的应用 1.1 基础知识 1.2 synchronized 语法 2. Monitor概念 3. Synchronized原理进阶 3.1 对象头格式 3.2 ...
- Java并发编程AQS详解
本文内容及图片代码参考视频:https://www.bilibili.com/video/BV12K411G7Fg/?spm_id_from=333.788.recommend_more_video. ...
- Java并发编程synchronized详解
一.关于临界区.临界资源.竞态条件和解决方法 首先看如下代码,thread1对变量i++做500次运算,thread2对i--做500次运算,但是最终的结果却可能为是正数,负数,0不一样的结果. pa ...
- JUC并发编程系列详解篇十四(自旋锁 VS 适应性自旋锁)
自旋锁 由于在多处理器环境中某些资源的有限性,有时需要互斥访问(mutual exclusion),这时候就需要引入锁的概念,只有获取了锁的线程才能够对资源进行访问,由于多线程的核心是CPU的时间分片 ...
- Java并发编程——ConcurrentHashMap详解
引出 场景:针对用户来做一个访问次数的记录. 通过HashMap进行记录,key为用户名,value为访问次数. public class ConcurrentHashMapDemo {private ...
- Java并发编程——ForkJoin详解
概念 Fork/Join 框架是 Java7 提供了的一个用于并行执行任务的框架, 是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架.类似于Java 8中的paralle ...
- 看完之后保证你对socket编程步骤胸有成竹。 C++ Socket网络编程基础详解(TCP)
C++ Socket网络编程基础详解(TCP版) 网络编程,就是编写程序使得两台计算机交换数据,其实从本质上来讲,网络编程最终所实现的功能,和我们文件的输入输出很相似,只是文件输入输出的对象 ...
最新文章
- 概率论中指数分布介绍及C++11中std::exponential_distribution的使用
- python学习第四课
- python大学教程吕云翔课后题答案_软件工程实用教程吕云翔-课后答案.doc
- kafka消息存储格式
- Jquery操作对控件的取值、赋值
- Fragment 和 FragmentActivity的使用(二)
- 2019校招Android面试题解1.0
- qi接收启动协议_基于QI协议的无线充电通信系统
- java-画出二维码
- U盘不被电脑识别问题
- 商业贷款和公积金贷款差多少?一组数据告诉你!
- J2EE疑难解决实例--转自JSP中文网
- SSID, BSSID, ESSID
- Vijos1234 口袋的天空 题解
- 判断访问来源是pc端还是手机端
- 麒麟操作系统V10安装达梦数据库
- c# json 按照key顺序排序。。。
- Ubuntu 下安装 StarUML
- 壁虎书1 The Machine Learning Landscape
- 如何操作拆分Word文档?干货来啦!怎样上下拆分Word文档?