Reputation: 146
I was wondering if there was any way to perform a compare-and-swap operation on static fields in Java, using Unsafe
, more precisely compareAndSwapObject
. I attempted using null
and the class object (TheClass.class
) as a first argument, but it didn't work.
Edit: Here's the code:
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
}
}
I wanted it to perform a CAS on the field staticField
, setting it to 350 (arbitrary value). What happened: when printing its value on the standard output, it displayed 0; so I suppose that nothing changed and that neither of those is the correct argument to do what I want to, assuming it is possible.
Thank you in advance!
Upvotes: 1
Views: 369
Reputation: 718758
One problem is that you are using compareAndSwapObject
on a field that is not a reference type. Since the field is a long
, you should be using compareAndSwapLong
.
Also, the compareAndSwapXXX
methods return a boolean
to say if the swap succeeded or failed. You should check it ... if there is any possibility that some other thread might do a CAS operation on the same field. (And if there is no possibility, don't bother with CAS. Just use regular assignments.)
Finally, I looked at the Java 8 source code, and I don't think that the Unsafe.compareAndSwapXxx
operations are designed to work on static
fields using a Class
object as the target object. Instead, I think you need to do something like this:
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);
Either way, there is no checking that the off
value is correct for the field of the object that you do the CAS operation on. So if you get it wrong, you are liable to get memory corruption (and incorrect results or a JVM crash) ... rather than a nice clean Java exception. This is the risk you take by using Unsafe
!
Having said all of the above, any code that uses Unsafe
code will be non-portable. And your code will fail for Java 9 and later because the compareAndSwapXxx
methods have been removed.
The correct way to do this in Java 9 and later will be to use the VarHandle
class; see the javadoc.
Upvotes: 3