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.
我想知道是否有任何方法可以使用 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.