如下代码主要展示了如何使用 CAS(Compare and Swap)操作来实现多线程并发控制。
在这个例子中,CAS 主要用于修改对象中的 status
和 name
字段。
在 Java 中,CAS 操作是通过 Unsafe
类来实现的,它可以直接操作对象的内存,而不受 Java 内存模型的限制。在这段代码中,通过 Unsafe
类获取了 status
和 name
字段在对象中的偏移量 statusOffset
和 nameOffSet
,然后通过 CAS 操作来修改这些字段的值。
为什么要通过偏移量来进行 CAS 操作呢?这是因为 CAS 操作是基于对象内存地址进行的,而不是对象的引用或名称。通过偏移量,可以直接确定对象内部字段在内存中的位置,从而正确地执行 CAS 操作。
在这段代码中,通过获取 status
和 name
字段在对象中的偏移量,可以在多线程环境下安全地对这些字段进行修改,而不会出现数据竞争或错误修改的情况。
需要注意的是,直接操作对象内存是一种底层的编程技巧,需要谨慎使用,并且在高级应用中建议使用更高级的并发工具和模式来实现并发控制。
public class CASTest {
private volatile int status;
private static final Unsafe unsafe;
// 对于 Java AbstractQueuedSynchronizer (AQS) 中的 stateOffset 字段,
// 通常会使用 static final 修饰是因为这个偏移量值在整个类中都是不变的,且所有实例都需要使用相同的偏移量来进行 CAS 操作。
// 即:不管有多少个 Bean 对象,对于Bean对象中的status操作的偏移量值是固定的,可以使用 static final
// statusOffset 只是一个相对的偏移量,跟status具体的值无关,所以可以多实例共享statusOffset
private static final long statusOffset;
private volatile String name;
private static final long nameOffSet;
static {
try {
// sun.misc 中的class无法像AQS中那样通过 private static final Unsafe unsafe = Unsafe.getUnsafe(); 直接调用
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
statusOffset = unsafe.objectFieldOffset(CASTest.class.getDeclaredField("status"));
nameOffSet = unsafe.objectFieldOffset(CASTest.class.getDeclaredField("name"));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
CASTest casTest = new CASTest();
System.out.println("statusOffset:" + CASTest.statusOffset);
System.out.println("status:" + casTest.status);
System.out.println("name:" + casTest.name);
System.out.println("nameOffSet:" + CASTest.nameOffSet);
// 多线程并发cas操作status 0->1
for (int i = 0; i < 10; i++) {
int finalI = i;
new Thread(() -> {
boolean nameSetSuccess = unsafe.compareAndSwapObject(casTest, nameOffSet, null, "张三");
if (nameSetSuccess) {
System.out.println(finalI + "==============-name:===============" + casTest.name);
System.out.println(finalI + "============-nameOffSet:================" + nameOffSet);
} else {
System.out.println(finalI + "-name:" + casTest.name);
System.out.println(finalI + "-nameOffSet:" + nameOffSet);
}
boolean success = unsafe.compareAndSwapInt(casTest, statusOffset, 0, 1);
if (success) {
System.out.println(finalI + "=============-statusOffset:===========" + statusOffset);
System.out.println(finalI + "=============-status:=============" + casTest.status);
} else {
System.out.println(finalI + "-statusOffset:" + statusOffset);
System.out.println(finalI + "-status:" + casTest.status);
}
}).start();
}
}
}
这段代码主要展示了如何使用 CAS(Compare and Swap)操作来实现多线程并发控制,并通过偏移量来操作对象中的字段。下面是对代码的分析及流程讲解:
-
首先定义了一个
CASTest
类,其中包含了一个volatile int status
和一个volatile String name
字段,以及相应的静态变量Unsafe unsafe
、long statusOffset
和long nameOffSet
。 -
在静态代码块中,通过反射的方式获取
Unsafe
对象,并获取了status
和name
字段在内存中的偏移量statusOffset
和nameOffSet
。 -
在
main
方法中,创建了一个CASTest
对象casTest
,并打印了statusOffset
、status
、name
和nameOffSet
的值。 -
接着使用了一个循环创建了10个线程,每个线程内部执行了以下操作:
- 使用
Unsafe.compareAndSwapObject
方法尝试将name
字段由null
修改为"张三"
,并根据操作结果输出相关信息。 - 使用
Unsafe.compareAndSwapInt
方法尝试将status
字段由0
修改为1
,并根据操作结果输出相关信息。
- 使用
-
在多线程并发操作中,每个线程都尝试使用 CAS 操作来修改
name
和status
字段的值。由于 CAS 是一种乐观锁的机制,会比较对象当前值和期望值,如果一致则进行更新,否则不会更新并返回失败。 -
通过输出结果可以观察到多线程并发时对
name
和status
字段的操作情况,以及 CAS 操作的成功与失败情况。
总体来说,这段代码演示了如何使用 CAS 操作和偏移量来实现多线程并发控制,尤其是在底层操作对象内存时的一种实践。需要注意的是,对于普通业务开发而言,推荐使用更高级的并发工具和模式来确保数据安全性和可靠性。
结果展示: