Java原子类之AtomicMarkableReference源码分析
一、简介
AtomicStampedReference
可以给引用加上版本号,追踪引用的整个变化过程,如:A -> B -> C -> D - > A
,通过AtomicStampedReference
可以知道引用变量中途被更改了3
次。但是有时候不需要关心引用变量更改了几次,只是单纯的关心是否更改过,所以就有了AtomicMarkableReference。
AtomicMarkableReference
和AtomicStampedReference
源码几乎相同,唯一区别就在于一个是int
型的时间戳,而这个类则是布尔型的标记值。
两者区别在于AtomicStampedReference
可以知道修改了多少次,而AtomicMarkableReference
则只知道有没有被修改过。
二、源码分析
/**
* 在原子变量类中 对于引用类型的操作,用于解决CAS的ABA问题
* AtomicMarkableReference:带boolean类型的引用型原子量 每次执行CAS操作都需要比较该标记位
* 若版本吗,满足要求 则操作成功 版本不满足要求 则操作失败
* 表示引用变量是否被更改过
*/
public class AtomicMarkableReference<V> {
/**
* 内部维护的[reference,boolean]二元组 AtomicMarkableReference的相关操作是对Pair内成员的操作
*/
private static class Pair<T> {
final T reference;
final boolean mark;
//构造方法
private Pair(T reference, boolean mark) {
this.reference = reference;
this.mark = mark;
}
//创建Pair实例-类加载时
static <T> Pair<T> of(T reference, boolean mark) {
return new Pair<T>(reference, mark);
}
}
/**
* 多线程共享变量
*/
private volatile Pair<V> pair;
/**
* 创建AtomicMarkableReference.Pair实例
*/
public AtomicMarkableReference(V initialRef, boolean initialMark) {
pair = Pair.of(initialRef, initialMark);
}
/**
* 以原子方式获取当前引用值
*/
public V getReference() {
return pair.reference;
}
/**
* 以原子方式获取当前标记
*/
public boolean isMarked() {
return pair.mark;
}
/**
* 以原子的方式获取当前的引用值和标记
*/
public V get(boolean[] markHolder) {
Pair<V> pair = this.pair;
markHolder[ 0 ] = pair.mark;
return pair.reference;
}
/**
* 比较并交换新值
*/
public boolean weakCompareAndSet(V expectedReference,
V newReference,
boolean expectedMark,
boolean newMark) {
return compareAndSet(expectedReference, newReference,
expectedMark, newMark);
}
/**
* CAS 比较并设置新值
* 期望引用 != 当前引用 返回false
* 期望标记 != 当前标记 返回false
* 期望标记和期望引用 = 当前值的前提下 新的标记和新的引用 = 当前值时 不更新 返回true
* 期望标记和期望引用 = 当前值的前提下 新的标记和新的引用 != 当前值时 设置新的引用和新的标记 返回true
* @param expectedReference 期望的引用
* @param newReference 新引用
* @param expectedMark 期望的标记
* @param newMark 新的标记
* @return 若成功 返回true
*/
public boolean compareAndSet(V expectedReference, V newReference, boolean expectedMark,
boolean newMark) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedMark == current.mark &&
((newReference == current.reference &&
newMark == current.mark) ||
casPair(current, Pair.of(newReference, newMark)));
}
/**
* 设置引用和标记 有一个和当前值不一样就进行更新
*/
public void set(V newReference, boolean newMark) {
Pair<V> current = pair;
if (newReference != current.reference || newMark != current.mark) {
pair = Pair.of(newReference, newMark);
}
}
/**
* 当期望值
* @param expectedReference 期望引用
* @param newMark 新的标记
* @return {@code true} if successful
*/
public boolean attemptMark(V expectedReference, boolean newMark) {
Pair<V> current = pair;
return expectedReference == current.reference &&
(newMark == current.mark ||
casPair(current, Pair.of(expectedReference, newMark)));
}
// Unsafe mechanics
/**
* 获取Unsafe实例
*/
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
/**
* 获取pair偏移量
*/
private static final long pairOffset = objectFieldOffset(UNSAFE,
"pair", AtomicMarkableReference.class);
/**
* 原子的交换两个对象(CAS交换)
* @param cmp
* @param val
* @return
*/
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
String field, Class<?> klazz) {
try {
return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
} catch (NoSuchFieldException e) {
NoSuchFieldError error = new NoSuchFieldError(field);
error.initCause(e);
throw error;
}
}
}
可以看到,AtomicMarkableReference
的唯一区别就是不再用int
标识引用,而是使用boolean
变量——表示引用变量是否被更改过。从语义上讲,AtomicMarkableReference
对于那些不关心引用变化过程,只关心引用变量是否变化过的应用会更加友好。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器