代码实现
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class MyAtomicLong {
private static final Unsafe unsafe;
private static final long valueOffset;
private volatile long value;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
valueOffset = unsafe.objectFieldOffset
(MyAtomicLong.class.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
public MyAtomicLong(long initialValue) {
value = initialValue;
}
public long get() {
return value;
}
public void set(long newValue) {
value = newValue;
}
public long incrementAndGet() {
for (; ; ) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next)) {
return next;
}
}
}
public long getAndIncrement() {
for (; ; ) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next)) {
return current;
}
}
}
public boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
}
设计思路解释
- 获取Unsafe实例:通过反射获取
Unsafe
类中的theUnsafe
字段,从而得到Unsafe
的实例。Unsafe
类提供了底层的CAS操作等功能。这一步通过static
代码块实现,保证在类加载时就获取Unsafe
实例。
- 获取字段偏移量:使用
unsafe.objectFieldOffset
方法获取value
字段在内存中的偏移量valueOffset
。这个偏移量用于后续的CAS操作,告诉Unsafe
在哪个内存位置进行操作。
- 定义成员变量:定义一个
volatile
修饰的long
型变量value
,用于存储实际的值。volatile
关键字保证了内存可见性,即一个线程对value
的修改,其他线程能够立即看到。
- 构造函数:
MyAtomicLong(long initialValue)
构造函数用于初始化value
的值。
- get方法:
get()
方法直接返回value
的值,因为value
是volatile
的,所以能保证获取到最新的值。
- set方法:
set(long newValue)
方法直接设置value
的值。
- incrementAndGet方法:通过无限循环,先获取当前值
current
,然后计算下一个值next = current + 1
,接着使用compareAndSet
方法尝试将value
从current
更新为next
。如果更新成功,说明没有其他线程同时修改value
,则返回next
;如果更新失败,说明有其他线程同时修改了value
,则继续循环重试。
- getAndIncrement方法:逻辑与
incrementAndGet
类似,不同的是先返回当前值current
,再尝试更新value
为next
。
- compareAndSet方法:调用
unsafe.compareAndSwapLong
方法进行CAS操作,尝试将对象this
中偏移量为valueOffset
处的值从expect
更新为update
,如果更新成功返回true
,否则返回false
。
可能存在的问题
- 反射获取Unsafe实例:通过反射获取
Unsafe
实例是一种非标准的做法,因为Unsafe
类在设计上是不希望被普通应用程序直接使用的。在不同的JVM版本中,Unsafe
类的实现和theUnsafe
字段的访问方式可能会有所变化,这可能导致代码在不同环境下的兼容性问题。
- 性能问题:在高并发场景下,
compareAndSet
操作可能会频繁失败,导致大量的循环重试,这会消耗大量的CPU资源。可以考虑使用更复杂的算法,如乐观锁机制结合分段锁等方式来提高性能。
- ABA问题:标准的CAS操作存在ABA问题,即一个值从A变为B,再变回A,CAS操作会认为这个值没有发生变化。虽然在这个简单的自增场景下ABA问题影响不大,但如果是更复杂的业务场景,可能需要额外的处理,比如使用带有版本号的原子操作。