Java - 比较和交换静态字段

Java - Compare-and-swap over a static field

我想知道是否有任何方法可以使用 Unsafe,更准确地说 compareAndSwapObject 对 Java 中的静态字段执行比较和交换操作。我尝试使用 null 和 class 对象 (TheClass.class) 作为第一个参数,但没有成功。

编辑: 这是代码:

public class UnsafeTest
{
 
    public static long staticField = 0;
 
    public static void main(String[] args) throws Exception
    {
        Long l = 0L;
        Long res = 350L;
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Unsafe unsafe = (Unsafe) f.get(null);
        long off = unsafe.staticFieldOffset(
                UnsafeTest.class.getDeclaredField("staticField"));
        unsafe.compareAndSwapObject(UnsafeTest.class, off, l, res);

        // null instead of UnsafeTest.class produces the same result
        Thread.sleep(1000L);
        System.out.println(staticField); // prints 0
    }

}

我希望它在字段 staticField 上执行 CAS,将其设置为 350(任意值)。发生了什么:在标准输出上打印它的值时,它显示为 0;所以我想什么都没有改变,而且这些都不是做我想做的事情的正确论据,假设这是可能的。

提前致谢!

一个问题是您在非引用类型的字段上使用 compareAndSwapObject。由于该字段是 long,您应该使用 compareAndSwapLong.

此外,compareAndSwapXXX 方法 return 和 boolean 表明交换是成功还是失败。你应该检查它......如果有任何其他线程可能在同一字段上执行 CAS 操作的可能性。 (如果没有可能,请不要为 CAS 而烦恼。只需使用常规分配即可。)

最后,我查看了 Java 8 源代码,我认为 Unsafe.compareAndSwapXxx 操作不是设计用于使用 [=18] 的 static 字段=] 对象作为目标对象。相反,我认为你需要做这样的事情:

    Field f = Unsafe.class.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    Unsafe unsafe = (Unsafe) f.get(null);
    long off = unsafe.staticFieldOffset(
            UnsafeTest.class.getDeclaredField("staticField"));
    Object target = unsafe.staticFieldBase(f);
    unsafe.compareAndSwapLong(target, off, 0L, 350L);

无论哪种方式,都不会检查 off 值对于您执行 CAS 操作的对象的字段是否正确。所以如果你弄错了,你很可能会导致内存损坏(以及不正确的结果或 JVM 崩溃)......而不是一个干净的 Java 异常。这是您使用 Unsafe!

所承担的风险

综上所述,任何使用 Unsafe 代码的代码都是不可移植的。并且您的代码将在 Java 9 及更高版本中失败,因为 compareAndSwapXxx 方法已被删除。

在 Java 9 及更高版本中执行此操作的正确方法是使用 VarHandle class;见 javadoc.