Creart
Creart

Reputation: 146

Java - Compare-and-swap over a static field

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

Answers (1)

Stephen C
Stephen C

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

Related Questions