【问题标题】:Java - Compare-and-swap over a static fieldJava - 在静态字段上进行比较和交换
【发布时间】:2021-08-07 14:05:19
【问题描述】:

我想知道是否有任何方法可以使用Unsafe,更准确地说是compareAndSwapObject,对 Java 中的静态字段执行比较和交换操作。我尝试使用 null 和类对象 (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;所以我想什么都没有改变,而且这些都不是做我想做的事情的正确论据,假设这是可能的。

提前谢谢你!

【问题讨论】:

    标签: java concurrency jvm compare-and-swap


    【解决方案1】:

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

    另外,compareAndSwapXXX 方法返回一个boolean 来说明交换是成功还是失败。你应该检查一下......如果有任何其他线程可能在同一字段上执行 CAS 操作。 (如果没有可能,请不要理会 CAS。只需使用常规分配即可。)

    最后,我查看了 Java 8 源代码,我不认为 Unsafe.compareAndSwapXxx 操作旨在使用 Class 对象作为目标对象来处理 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 类;见javadoc

    【讨论】:

    • 内存损坏最糟糕的不是潜在的 JVM 崩溃。可能发生的最糟糕的事情是应用程序继续使用错误但合理的数据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多