1. 写在前面
AtomicLong 是 Java 中提供的一个用于处理高效并发编程的类。它提供了一些原子操作,确保在多线程环境下的安全性和高效性。按照惯例有几个问题大家看看有没有思考过:
- AtomicLong 和 Long 有什么区别?
- AtomicLong 的 compareAndSet 方法是如何工作的?
- 如何使用 AtomicLong 实现线程安全的计数器?
- AtomicLong 的优缺点是什么?
- AtomicLong 的 CAS 操作是如何保证线程安全的?
2. 全局视角
2.1 实现的接口
2.1.1 java.io.Serializable
使 AtomicLong 对象可以被序列化,以便在不同的 JVM 之间传输或持久化到存储中
2.1.2 java.lang.Number
使 AtomicLong 可以作为数值类型使用,并且可以与其他数值类型进行互操作
2.2 类的详细结构
2.2.1 内部字段
AtomicLong 使用一个 volatile 修饰的 long 字段来存储其值:
private volatile long value;
2.2.2 构造函数
AtomicLong 提供了两个构造函数:
- 默认构造函数,初始值为 0:
public AtomicLong() {
this.value = 0;
}
- 带初始值的构造函数
public AtomicLong(long initialValue) {
this.value = initialValue;
}
2.2.3 关键方法
- long get(): 获取当前值。
public final long get() {
return value;
}
- void set(long newValue): 设置新值。
public final void set(long newValue) {
value = newValue;
}
- long getAndSet(long newValue): 获取当前值并设置新值。
public final long getAndSet(long newValue) {
return UNSAFE.getAndSetLong(this, valueOffset, newValue);
}
- boolean compareAndSet(long expect, long update): 如果当前值等于预期值,则原子地将其设置为新值。
public final boolean compareAndSet(long expect, long update) {
return UNSAFE.compareAndSwapLong(this, valueOffset, expect, update);
}
- long getAndIncrement(): 获取当前值并自增。
public final long getAndIncrement() {
return getAndAdd(1);
}
- long incrementAndGet(): 自增并获取新值。
public final long incrementAndGet() {
return addAndGet(1);
}
- long getAndDecrement(): 获取当前值并自减。
public final long getAndDecrement() {
return getAndAdd(-1);
}
- long decrementAndGet(): 自减并获取新值。
public final long decrementAndGet() {
return addAndGet(-1);
}
- long getAndAdd(long delta): 获取当前值并增加指定值。
public final long getAndAdd(long delta) {
return UNSAFE.getAndAddLong(this, valueOffset, delta);
}
- long addAndGet(long delta): 增加指定值并获取新值。
public final long addAndGet(long delta) {
return UNSAFE.getAndAddLong(this, valueOffset, delta) + delta;
}
2.3 使用 Unsafe 类
AtomicLong 的实现依赖于 sun.misc.Unsafe 类来执行低级别的、硬件支持的原子操作。Unsafe 类提供了直接操作内存和执行原子操作的方法,这些方法通常是由 JVM 内部使用的。
例如,compareAndSet 方法使用 Unsafe 类的 compareAndSwapLong 方法来实现原子操作:
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = UNSAFE.objectFieldOffset(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
public final boolean compareAndSet(long expect, long update) {
return UNSAFE.compareAndSwapLong(this, valueOffset, expect, update);
}
3. AtomicLong 和 Long 有什么区别?
- AtomicLong 提供了原子操作,确保在多线程环境下的线程安全性。
- Long 是不可变对象,不能在多线程环境下进行安全的更新操作。
4. AtomicLong 的 compareAndSet 方法是如何工作的?
compareAndSet 方法使用了硬件支持的原子指令(如 CAS,Compare-And-Swap)来实现。它的工作原理是:
- 如果当前值等于预期值,则将其原子地更新为新值。
- 如果当前值不等于预期值,则不更新,并返回 false。
AtomicLong atomicLong = new AtomicLong(100L);
boolean success = atomicLong.compareAndSet(100L, 200L); // 如果当前值是100L,则更新为200L
5. 如何使用 AtomicLong 实现线程安全的计数器?
可以使用 AtomicLong 实现线程安全的计数器,如下所示:
public class AtomicCounter {
private AtomicLong counter = new AtomicLong();
public void increment() {
counter.incrementAndGet();
}
public long getValue() {
return counter.get();
}
}
6. AtomicLong 的优缺点是什么?
6.1 优点
- 提供了高效的原子操作,避免了使用锁(如 synchronized)带来的性能开销。
- 使用简单,线程安全。
6.2 缺点
- 只能处理单一变量的原子操作,对于复杂的并发控制可能不够。
- 在高竞争的情况下,CAS 操作可能会导致大量的重试,影响性能。
7. AtomicLong 与 LongAdder 有什么区别?
- AtomicLong 使用 CAS 操作来保证线程安全,适用于低竞争的场景。
- LongAdder 通过分段计数来减少竞争,适用于高竞争的场景。
8. AtomicLong 的 CAS 操作是如何保证线程安全的?
AtomicLong 的 CAS 操作(Compare-And-Swap)是由底层硬件支持的原子指令实现的。它的工作流程如下:
- 获取当前值。
- 比较当前值与预期值。
- 如果当前值等于预期值,则将其更新为新值。
- 如果当前值不等于预期值,则不更新,并返回 false。
CAS 操作是原子的,即使在多线程环境下也能保证操作的完整性和一致性。
系列文章
7.jdk源码阅读之ConcurrentHashMap(上)
8.jdk源码阅读之ConcurrentHashMap(下)
17.jdk源码阅读之LinkedBlockingQueue