java.util.concurrent.atomic
包提供了一组基于 CAS(Compare-And-Swap) 操作的线程安全类,用于在多线程环境中以原子方式操作变量。这些类通过硬件级别的原子操作(CAS)实现线程安全,避免了传统锁机制的开销。
1. Atomic 类的分类
java.util.concurrent.atomic
包下的主要 Atomic 类可以分为以下几类:
1.1 基本类型的 Atomic 类
AtomicBoolean
:用于以原子方式操作布尔值(boolean
)。AtomicInteger
:用于以原子方式操作整数(int
)。AtomicLong
:用于以原子方式操作长整数(long
)。
1.2 引用类型的 Atomic 类
AtomicReference<V>
:用于以原子方式操作引用类型的变量。AtomicStampedReference<V>
:用于以原子方式操作引用类型的变量,同时解决 CAS 的 ABA 问题。AtomicMarkableReference<V>
:用于以原子方式操作引用类型的变量,并附带一个布尔标志位。
1.3 数组类型的 Atomic 类
AtomicIntegerArray
:用于以原子方式操作int
类型的数组。AtomicLongArray
:用于以原子方式操作long
类型的数组。AtomicReferenceArray<E>
:用于以原子方式操作引用类型的数组。
1.4 累加器和计数器
LongAdder
和DoubleAdder
:用于高效地累加long
和double
类型的值,适合高并发场景。LongAccumulator
和DoubleAccumulator
:支持自定义累加函数的高效累加器。
2. 常用 Atomic 类及其方法
以下是每个常用 Atomic 类的详细介绍,包括常用方法及其原子性保证原理。
2.1 AtomicBoolean
作用:以原子方式操作布尔值(boolean
)。
常用方法:
-
get()
:获取当前值。 -
set(boolean newValue)
:设置新值。 -
compareAndSet(boolean expect, boolean update)
:- 如果当前值等于
expect
,则更新为update
,否则不更新。
- 如果当前值等于
-
getAndSet(boolean newValue)
:- 将值设置为
newValue
,并返回旧值。
- 将值设置为
原子性保证:
- 通过底层的 CAS 操作(
compareAndSwapInt
)实现。 - CAS 操作依赖于硬件指令(如 x86 的
CMPXCHG
或 ARM 的LDREX/STREX
),能够在硬件层面保证比较和更新操作的原子性。
2.2 AtomicInteger
作用:以原子方式操作整数(int
)。
常用方法:
-
get()
:获取当前值。 -
set(int newValue)
:设置新值。 -
compareAndSet(int expect, int update)
:- 如果当前值等于
expect
,则更新为update
,否则不更新。
- 如果当前值等于
-
getAndIncrement()
:- 将当前值加 1,并返回旧值。
-
incrementAndGet()
:- 将当前值加 1,并返回新值。
-
getAndAdd(int delta)
:- 将当前值加上
delta
,并返回旧值。
- 将当前值加上
-
addAndGet(int delta)
:- 将当前值加上
delta
,并返回新值。
- 将当前值加上
原子性保证:
- 通过底层的 CAS 操作(
compareAndSwapInt
)实现。 - 如果 CAS 操作失败,方法会进入自旋状态,直到更新成功。
2.3 AtomicLong
作用:以原子方式操作长整数(long
)。
常用方法:
-
get()
:获取当前值。 -
set(long newValue)
:设置新值。 -
compareAndSet(long expect, long update)
:- 如果当前值等于
expect
,则更新为update
,否则不更新。
- 如果当前值等于
-
getAndIncrement()
:- 将当前值加 1,并返回旧值。
-
incrementAndGet()
:- 将当前值加 1,并返回新值。
-
getAndAdd(long delta)
:- 将当前值加上
delta
,并返回旧值。
- 将当前值加上
-
addAndGet(long delta)
:- 将当前值加上
delta
,并返回新值。
- 将当前值加上
原子性保证:
- 与
AtomicInteger
类似,依赖底层的 CAS 操作(compareAndSwapLong
)。
2.4 AtomicReference<V>
作用:以原子方式操作引用类型的变量。
常用方法:
-
get()
:获取当前引用。 -
set(V newValue)
:设置新引用。 -
compareAndSet(V expect, V update)
:- 如果当前引用等于
expect
,则更新为update
,否则不更新。
- 如果当前引用等于
-
getAndSet(V newValue)
:- 将引用设置为
newValue
,并返回旧引用。
- 将引用设置为
原子性保证:
- 通过底层的 CAS 操作(
compareAndSwapObject
)实现。
2.5 AtomicStampedReference<V>
作用:以原子方式操作引用类型的变量,同时解决 CAS 的 ABA 问题。
常用方法:
-
getReference()
:获取当前引用。 -
getStamp()
:获取当前版本号(Stamp)。 -
compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
:- 如果当前引用等于
expectedReference
且版本号等于expectedStamp
,则更新为newReference
和newStamp
。
- 如果当前引用等于
-
get()
:- 同时获取引用和版本号。
原子性保证:
- 在引用的基础上增加了版本号,通过版本号解决 ABA 问题。
2.6 AtomicIntegerArray
作用:以原子方式操作 int
类型的数组。
常用方法:
-
get(int index)
:获取指定索引的值。 -
set(int index, int newValue)
:设置指定索引的值。 -
compareAndSet(int index, int expect, int update)
:- 如果指定索引的值等于
expect
,则更新为update
。
- 如果指定索引的值等于
-
getAndIncrement(int index)
:- 将指定索引的值加 1,并返回旧值。
原子性保证:
- 每个数组元素的操作通过 CAS 实现,保证线程安全。
3. 原子性保证的原理
1. CAS(Compare-And-Swap)
-
核心思想:
- 比较内存中的值是否等于预期值(
expected
)。 - 如果相等,则将内存中的值更新为新值(
update
)。 - 如果不相等,则不更新,并返回失败。
- 比较内存中的值是否等于预期值(
-
硬件支持:
- CAS 操作依赖于 CPU 提供的原子指令(如 x86 的
CMPXCHG
或 ARM 的LDREX/STREX
)。 - 这些指令能够在硬件层面保证比较和更新操作的原子性。
- CAS 操作依赖于 CPU 提供的原子指令(如 x86 的
2. 自旋重试
- 如果 CAS 操作失败,Atomic 类的方法会进入自旋状态,重新尝试,直到操作成功。
3. 不可分割性
- CAS 操作是不可分割的,能够在多线程环境下保证数据的一致性。
4. 优缺点
优点
-
线程安全:
- 通过 CAS 操作实现线程安全,无需显式加锁。
-
高性能:
- 避免了传统锁机制的开销(如线程阻塞和上下文切换)。
-
简单易用:
- 提供了原子操作的封装,API 简单。
缺点
-
自旋开销:
- 如果 CAS 操作失败,可能导致自旋重试,浪费 CPU 资源。
-
ABA 问题:
- 在某些场景下,CAS 操作可能会遇到 ABA 问题,需要额外处理(如使用
AtomicStampedReference
)。
- 在某些场景下,CAS 操作可能会遇到 ABA 问题,需要额外处理(如使用
5. 总结
-
java.util.concurrent.atomic
包的作用:- 提供了一组线程安全的类,用于以原子方式操作变量。
-
常用类:
AtomicBoolean
、AtomicInteger
、AtomicLong
、AtomicReference
等。
-
原子性保证:
- 通过底层的 CAS 操作和自旋重试机制实现。
-
适用场景:
- 高并发环境下的计数器、标志位、引用更新等。
getAndSet 方法是如何保证原子性的
getAndSet
方法的原子性保证
getAndSet
是 Java 并发包中 Atomic
类(如 AtomicBoolean
、AtomicInteger
、AtomicReference
等)提供的一个方法,用于以原子方式将变量设置为新值,并返回设置前的旧值。
方法定义
以 AtomicBoolean
为例,getAndSet
方法的定义如下:
public final boolean getAndSet(boolean newValue) {
boolean prev;
do {
prev = get(); // 获取当前值
} while (!compareAndSet(prev, newValue)); // CAS 操作
return prev; // 返回旧值
}
getAndSet
的作用
-
功能:
- 将变量的值设置为
newValue
。 - 返回设置前的旧值。
- 将变量的值设置为
-
线程安全:
- 通过底层的 CAS(Compare-And-Swap) 操作保证线程安全。
-
原子性:
- 整个操作(获取旧值并设置新值)是不可分割的,多个线程同时调用时不会出现竞态条件。
2. getAndSet
的实现原理
1. 基于 CAS 实现
getAndSet
的核心是通过 CAS(Compare-And-Swap) 操作实现的。CAS 是一种硬件级别的原子操作,能够在多线程环境下安全地比较和更新变量的值。
2. 工作流程
以下是 getAndSet
的工作流程:
-
获取当前值:
- 调用
get()
方法读取当前值。
- 调用
-
尝试更新值:
- 使用
compareAndSet
方法尝试将当前值更新为新值。 - 如果当前值未被其他线程修改,则更新成功。
- 如果当前值已被其他线程修改,则重试。
- 使用
-
返回旧值:
- 当 CAS 操作成功时,返回旧值。
3. 自旋重试
- 如果 CAS 操作失败(即当前值已被其他线程修改),
getAndSet
会进入自旋状态,重新获取当前值并重试,直到更新成功。
3. getAndSet
的原子性保证
1. CAS 的原子性
- CAS 操作依赖于底层硬件指令(如 x86 架构的
CMPXCHG
指令或 ARM 架构的LDREX/STREX
指令),这些指令能够在硬件层面保证比较和更新操作的原子性。 - 在多线程环境下,CAS 操作可以确保只有一个线程能够成功更新变量的值。
2. 自旋重试机制
-
如果 CAS 操作失败,
getAndSet
会通过自旋重试的方式确保操作最终成功。 -
自旋重试的逻辑如下:
- 获取当前值。
- 尝试更新值。
- 如果失败,则重新获取当前值并重试。
3. 不可分割性
getAndSet
的整个操作(获取旧值并设置新值)是不可分割的。- 即使多个线程同时调用
getAndSet
,每个线程都会看到一个一致的旧值,并成功更新变量的值。
4. 示例代码
以下是一个使用 AtomicBoolean
的 getAndSet
示例:
import java.util.concurrent.atomic.AtomicBoolean;
public class GetAndSetExample {
public static void main(String[] args) {
AtomicBoolean atomicBoolean = new AtomicBoolean(false);
// 使用 getAndSet 方法
boolean oldValue = atomicBoolean.getAndSet(true);
System.out.println("Old Value: " + oldValue); // 输出: false
System.out.println("New Value: " + atomicBoolean.get()); // 输出: true
}
}
执行流程
-
初始值为
false
。 -
调用
getAndSet(true)
:- 获取当前值
false
。 - 将值更新为
true
。 - 返回旧值
false
。
- 获取当前值
-
最终值为
true
。
5. 多线程场景中的行为
以下是一个多线程场景下使用 getAndSet
的示例:
import java.util.concurrent.atomic.AtomicBoolean;
public class MultiThreadGetAndSet {
private static final AtomicBoolean flag = new AtomicBoolean(false);
public static void main(String[] args) {
Runnable task = () -> {
boolean oldValue = flag.getAndSet(true);
System.out.println(Thread.currentThread().getName() + " - Old Value: " + oldValue);
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}
可能的输出
Thread-0 - Old Value: false
Thread-1 - Old Value: true
解释
-
初始值为
false
。 -
两个线程同时调用
getAndSet(true)
:- 第一个线程成功更新值,将值从
false
更新为true
,并返回旧值false
。 - 第二个线程读取到更新后的值
true
,并返回旧值true
。
- 第一个线程成功更新值,将值从
6. getAndSet
的优缺点
优点
-
线程安全:
- 通过 CAS 操作保证线程安全,无需显式加锁。
-
高性能:
- 避免了传统锁机制的开销(如线程阻塞和上下文切换)。
-
简单易用:
- 提供了原子操作的封装,API 简单。
缺点
-
自旋开销:
- 如果 CAS 操作失败,
getAndSet
会进入自旋状态,可能导致 CPU 资源浪费。
- 如果 CAS 操作失败,
-
适用场景有限:
- 仅适用于单个变量的原子更新,无法直接操作多个变量。
7. getAndSet
的底层实现
1. Unsafe
类的支持
getAndSet
的底层实现依赖于Unsafe
类的compareAndSwapXXX
方法。- 示例(以
AtomicBoolean
为例):
public final boolean getAndSet(boolean newValue) {
boolean prev;
do {
prev = get(); // 获取当前值
} while (!compareAndSet(prev, newValue)); // CAS 操作
return prev; // 返回旧值
}
2. 硬件指令的支持
compareAndSet
方法通过 JNI 调用底层硬件指令(如 x86 的CMPXCHG
或 ARM 的LDREX/STREX
)实现原子性。
8. 总结
-
getAndSet
的作用:- 以原子方式将变量设置为新值,并返回旧值。
-
原子性保证:
- 通过底层的 CAS 操作和自旋重试机制实现。
-
适用场景:
- 适用于需要以线程安全的方式更新单个变量的场景。
-
优缺点:
- 优点:线程安全、高性能、简单易用。
- 缺点:自旋可能导致 CPU 资源浪费,适用场景有限。
AtomicIntegerFieldUpdater
和 AtomicLongFieldUpdater
AtomicIntegerFieldUpdater
和 AtomicLongFieldUpdater
是 Java 并发包(java.util.concurrent.atomic
)中的两个工具类,用于以原子方式更新对象中指定的 int
或 long
类型的字段。这些类通过反射机制操作字段,并且可以避免直接使用锁来实现线程安全。
1. 为什么需要 AtomicIntegerFieldUpdater
和 AtomicLongFieldUpdater
?
-
节省内存:
- 如果直接使用
AtomicInteger
或AtomicLong
,需要为每个字段创建一个独立的对象。 - 使用
AtomicIntegerFieldUpdater
或AtomicLongFieldUpdater
可以直接操作对象的字段,避免额外的对象开销。
- 如果直接使用
-
高效更新:
- 它们通过 CAS(Compare-And-Swap)操作实现线程安全,性能高于传统的锁机制。
-
灵活性:
- 允许对现有类的字段进行原子操作,而无需修改类的定义(如将字段改为
AtomicInteger
)。
- 允许对现有类的字段进行原子操作,而无需修改类的定义(如将字段改为
2. 使用限制
-
字段必须是
volatile
修饰的:- 为了确保字段的可见性,
AtomicIntegerFieldUpdater
和AtomicLongFieldUpdater
要求被操作的字段必须用volatile
修饰。
- 为了确保字段的可见性,
-
字段不能是
static
的:- 这两个类只能操作实例字段,不能操作静态字段。
-
字段的访问权限:
- 如果字段是
private
或protected
,需要通过反射访问,调用者必须有足够的权限(如在同一个包中或通过设置Accessible
)。
- 如果字段是
3. AtomicIntegerFieldUpdater
的使用
定义
AtomicIntegerFieldUpdater
是一个工具类,用于以原子方式更新对象中 int
类型的字段。
常用方法
-
newUpdater(Class<T> tclass, String fieldName)
:-
创建一个
AtomicIntegerFieldUpdater
实例。 -
参数:
tclass
:包含目标字段的类。fieldName
:目标字段的名称。
-
-
get(T obj)
:- 获取目标对象中字段的当前值。
-
set(T obj, int newValue)
:- 设置目标对象中字段的新值。
-
compareAndSet(T obj, int expect, int update)
:- 如果字段的当前值等于
expect
,则将其更新为update
。
- 如果字段的当前值等于
-
getAndIncrement(T obj)
:- 将字段的值加 1,并返回旧值。
-
incrementAndGet(T obj)
:- 将字段的值加 1,并返回新值。
示例代码
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
class Person {
volatile int age; // 必须是 volatile 修饰的字段
public Person(int age) {
this.age = age;
}
}
public class AtomicIntegerFieldUpdaterExample {
public static void main(String[] args) {
// 创建一个 AtomicIntegerFieldUpdater 实例
AtomicIntegerFieldUpdater<Person> updater =
AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");
Person person = new Person(25);
// 获取当前值
System.out.println("Initial Age: " + updater.get(person)); // 输出: 25
// 更新值
updater.set(person, 30);
System.out.println("Updated Age: " + updater.get(person)); // 输出: 30
// CAS 操作
boolean success = updater.compareAndSet(person, 30, 35);
System.out.println("CAS Success: " + success); // 输出: true
System.out.println("Age after CAS: " + updater.get(person)); // 输出: 35
// 自增操作
int oldAge = updater.getAndIncrement(person);
System.out.println("Old Age: " + oldAge); // 输出: 35
System.out.println("New Age: " + updater.get(person)); // 输出: 36
}
}
4. AtomicLongFieldUpdater
的使用
定义
AtomicLongFieldUpdater
是一个工具类,用于以原子方式更新对象中 long
类型的字段。
常用方法
与 AtomicIntegerFieldUpdater
类似,AtomicLongFieldUpdater
提供的方法包括:
-
newUpdater(Class<T> tclass, String fieldName)
:- 创建一个
AtomicLongFieldUpdater
实例。
- 创建一个
-
get(T obj)
:- 获取目标对象中字段的当前值。
-
set(T obj, long newValue)
:- 设置目标对象中字段的新值。
-
compareAndSet(T obj, long expect, long update)
:- 如果字段的当前值等于
expect
,则将其更新为update
。
- 如果字段的当前值等于
-
getAndIncrement(T obj)
:- 将字段的值加 1,并返回旧值。
-
incrementAndGet(T obj)
:- 将字段的值加 1,并返回新值。
示例代码
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
class Account {
volatile long balance; // 必须是 volatile 修饰的字段
public Account(long balance) {
this.balance = balance;
}
}
public class AtomicLongFieldUpdaterExample {
public static void main(String[] args) {
// 创建一个 AtomicLongFieldUpdater 实例
AtomicLongFieldUpdater<Account> updater =
AtomicLongFieldUpdater.newUpdater(Account.class, "balance");
Account account = new Account(1000L);
// 获取当前值
System.out.println("Initial Balance: " + updater.get(account)); // 输出: 1000
// 更新值
updater.set(account, 1500L);
System.out.println("Updated Balance: " + updater.get(account)); // 输出: 1500
// CAS 操作
boolean success = updater.compareAndSet(account, 1500L, 2000L);
System.out.println("CAS Success: " + success); // 输出: true
System.out.println("Balance after CAS: " + updater.get(account)); // 输出: 2000
// 自增操作
long oldBalance = updater.getAndIncrement(account);
System.out.println("Old Balance: " + oldBalance); // 输出: 2000
System.out.println("New Balance: " + updater.get(account)); // 输出: 2001
}
}
5. 原子性保证的原理
1. CAS(Compare-And-Swap)
AtomicIntegerFieldUpdater
和AtomicLongFieldUpdater
的核心是 CAS 操作。- CAS 操作通过底层硬件指令(如 x86 的
CMPXCHG
或 ARM 的LDREX/STREX
)实现,能够在硬件层面保证比较和更新操作的原子性。
2. 反射机制
- 这两个类通过反射机制访问目标对象的字段。
- 在创建
Updater
时,newUpdater
方法会检查字段是否符合要求(如是否为volatile
),并通过反射获取字段的内存偏移量。
3. 内存偏移量
Updater
使用Unsafe
类的compareAndSwapXXX
方法,通过字段的内存偏移量直接操作内存,避免了传统的锁机制。
6. 优缺点
优点
-
节省内存:
- 避免了为每个字段创建独立的
AtomicInteger
或AtomicLong
对象。
- 避免了为每个字段创建独立的
-
高性能:
- 通过 CAS 操作实现线程安全,性能高于传统锁机制。
-
灵活性:
- 可以对现有类的字段进行原子操作,而无需修改类的定义。
缺点
-
使用限制:
- 字段必须是
volatile
修饰的,且不能是static
。
- 字段必须是
-
复杂性:
- 需要通过反射访问字段,可能导致代码复杂度增加。
-
安全性:
- 如果字段是
private
或protected
,需要确保调用者有足够的权限。
- 如果字段是
7. 总结
-
AtomicIntegerFieldUpdater
和AtomicLongFieldUpdater
的作用:- 提供了一种高效的方式,以原子方式更新对象中的
int
或long
类型字段。
- 提供了一种高效的方式,以原子方式更新对象中的
-
适用场景:
- 高并发环境下的计数器、状态标志等。
-
原子性保证:
- 通过 CAS 操作和反射机制实现。
-
优缺点:
- 节省内存、高性能,但使用限制较多。
AtomicReference
、AtomicStampedReference
和 AtomicMarkableReference
的详细介绍
这三个类都属于 java.util.concurrent.atomic
包,主要用于以原子方式操作引用类型的变量。它们的核心功能是基于 CAS(Compare-And-Swap) 操作实现线程安全,但它们的功能和适用场景有所不同。
1. AtomicReference
定义
AtomicReference<V>
是一个支持原子操作的类,用于操作引用类型的变量(如对象)。- 它允许在多线程环境中安全地更新对象引用,而无需显式加锁。
常用方法
get()
:
- 获取当前引用的值。
- 示例:
AtomicReference<String> atomicReference = new AtomicReference<>("Initial");
System.out.println(atomicReference.get()); // 输出: Initial
set(V newValue)
:
- 将引用设置为指定的新值。
- 示例:
atomicReference.set("Updated");
System.out.println(atomicReference.get()); // 输出: Updated
compareAndSet(V expectedValue, V newValue)
:
- 如果当前引用等于
expectedValue
,则将其更新为newValue
,否则不更新。 - 示例:
boolean result = atomicReference.compareAndSet("Initial", "New Value");
System.out.println(result); // 输出: false
System.out.println(atomicReference.get()); // 输出: Updated
getAndSet(V newValue)
:
- 将引用设置为
newValue
,并返回设置前的旧值。 - 示例:
String oldValue = atomicReference.getAndSet("Final Value");
System.out.println(oldValue); // 输出: Updated
System.out.println(atomicReference.get()); // 输出: Final Value
适用场景
- 用于简单的引用更新操作。
- 适合没有 ABA 问题 的场景。
缺点
- 无法解决 CAS 的 ABA 问题(即值从
A
变为B
,然后又变回A
,CAS 操作会认为值没有变化)。
2. AtomicStampedReference
定义
AtomicStampedReference<V>
是AtomicReference
的扩展,除了存储引用值外,还存储一个版本号(Stamp) 。- 它主要用于解决 CAS 操作中的 ABA 问题。
常用方法
getReference()
:
- 获取当前引用的值。
- 示例:
AtomicStampedReference<String> atomicStampedRef = new AtomicStampedReference<>("Initial", 1);
System.out.println(atomicStampedRef.getReference()); // 输出: Initial
getStamp()
:
- 获取当前的版本号(Stamp)。
- 示例:
System.out.println(atomicStampedRef.getStamp()); // 输出: 1
compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
:
- 如果当前引用等于
expectedReference
且版本号等于expectedStamp
,则将引用更新为newReference
,版本号更新为newStamp
。 - 示例:
boolean result = atomicStampedRef.compareAndSet("Initial", "Updated", 1, 2);
System.out.println(result); // 输出: true
System.out.println(atomicStampedRef.getReference()); // 输出: Updated
System.out.println(atomicStampedRef.getStamp()); // 输出: 2
get(int[] stampHolder)
:
- 同时获取引用值和版本号。
- 示例:
int[] stampHolder = new int[1];
String value = atomicStampedRef.get(stampHolder);
System.out.println("Value: " + value + ", Stamp: " + stampHolder[0]); // 输出: Value: Updated, Stamp: 2
适用场景
- 用于需要解决 ABA 问题 的场景。
- 例如:在多线程环境中,某个变量的值可能被其他线程多次修改并恢复为原值,导致 CAS 操作误判。
缺点
- 增加了版本号的管理,使用复杂度较高。
3. AtomicMarkableReference
定义
AtomicMarkableReference<V>
是AtomicReference
的扩展,除了存储引用值外,还存储一个布尔标志位(Mark) 。- 它主要用于标记引用的状态。
常用方法
getReference()
:
- 获取当前引用的值。
- 示例:
AtomicMarkableReference<String> atomicMarkableRef = new AtomicMarkableReference<>("Initial", false);
System.out.println(atomicMarkableRef.getReference()); // 输出: Initial
isMarked()
:
- 获取当前的标志位(Mark)。
- 示例:
System.out.println(atomicMarkableRef.isMarked()); // 输出: false
compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark)
:
- 如果当前引用等于
expectedReference
且标志位等于expectedMark
,则将引用更新为newReference
,标志位更新为newMark
。 - 示例:
boolean result = atomicMarkableRef.compareAndSet("Initial", "Updated", false, true);
System.out.println(result); // 输出: true
System.out.println(atomicMarkableRef.getReference()); // 输出: Updated
System.out.println(atomicMarkableRef.isMarked()); // 输出: true
get(boolean[] markHolder)
:
- 同时获取引用值和标志位。
- 示例:
boolean[] markHolder = new boolean[1];
String value = atomicMarkableRef.get(markHolder);
System.out.println("Value: " + value + ", Mark: " + markHolder[0]); // 输出: Value: Updated, Mark: true
适用场景
- 用于需要附加状态标记的场景。
- 例如:在并发环境中,标志位可以用来表示某个引用是否被逻辑删除。
缺点
- 增加了标志位的管理,使用复杂度较高。
4. 三者的对比
特性 | AtomicReference | AtomicStampedReference | AtomicMarkableReference |
---|---|---|---|
存储内容 | 引用值 | 引用值 + 版本号(Stamp) | 引用值 + 标志位(Mark) |
解决 ABA 问题 | 无法解决 | 通过版本号解决 | 无法解决 |
附加状态 | 无 | 版本号 | 布尔标志位 |
适用场景 | 简单的引用更新 | 需要解决 ABA 问题的场景 | 需要附加状态标记的场景 |
使用复杂度 | 简单 | 较复杂 | 较复杂 |
5. 示例场景
1. AtomicReference
的场景
- 简单的引用更新操作。
- 示例:实现线程安全的单例模式。
public class Singleton {
private static final AtomicReference<Singleton> instance = new AtomicReference<>();
private Singleton() {}
public static Singleton getInstance() {
if (instance.get() == null) {
instance.compareAndSet(null, new Singleton());
}
return instance.get();
}
}
2. AtomicStampedReference
的场景
- 解决 ABA 问题。
- 示例:模拟 ABA 问题的解决。
public class ABAProblem {
public static void main(String[] args) {
AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 1);
Thread t1 = new Thread(() -> {
int stamp = ref.getStamp();
ref.compareAndSet("A", "B", stamp, stamp + 1);
ref.compareAndSet("B", "A", stamp + 1, stamp + 2);
});
Thread t2 = new Thread(() -> {
int stamp = ref.getStamp();
boolean result = ref.compareAndSet("A", "C", stamp, stamp + 1);
System.out.println("Update success: " + result); // 输出: false
});
t1.start();
t2.start();
}
}
3. AtomicMarkableReference
的场景
- 附加状态标记。
- 示例:逻辑删除。
public class LogicalDeletion {
public static void main(String[] args) {
AtomicMarkableReference<String> ref = new AtomicMarkableReference<>("A", false);
// 标记为逻辑删除
ref.compareAndSet("A", "A", false, true);
System.out.println("Is marked: " + ref.isMarked()); // 输出: true
}
}
6. 总结
-
AtomicReference
:- 用于简单的引用更新操作。
- 无法解决 ABA 问题。
-
AtomicStampedReference
:- 在引用更新的基础上增加了版本号,用于解决 ABA 问题。
-
AtomicMarkableReference
:- 在引用更新的基础上增加了布尔标志位,用于附加状态标记。