Atomic Field Updaters

0

Brief about AtomicReferenceFieldUpdater

There are few data structures in Java concurrency APIs which are atomic in nature so there is also a need to update a field atomically. Class AtomicReferenceFieldUpdater is used for the atomic updates, for example in java concurrency data structures like SynchronousQueue and ConcurrentSkipListMap.

About Unsafe class

AtomicReferenceFieldUpdater uses Unsafe class to update the field. It also relies on reflection to do certain internal type checks.

Unsafe class belongs to sun.misc package. It has a collection of methods for performing low-level, unsafe operations. It directly updates the java memory based on the location of a given field in the storage allocation of its class.

Updating the field using AtomicReferenceFieldUpdater

The updater classes have no constructors; to create one, we call the newUpdater factory method, specifying the class and field name. The field updater classes are not tied to a specific instance; one can be used to update the target field for any instance of the target class.

For example:

AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(TestMe.class, Integer.class, "i");

Target class here  is an inner class:

private class TestMe {
    volatile Integer i;
}

To update the field ‘i’ we call the below. The second variable is the current state and the third is the new state:
updater.compareAndSet(t, null, 1);

Failure Scenarios

While updating the target object, the code needs to make sure the field is accessible from the caller’s class. The target object type and field type of the field must match with that of the passed in types while creating the  updater. Only when the basic screening fail that ‘instance Of’ check is done.

The target class and the caller class are not same and the field is private.

public class AtomicTests extends TestCase {
    public void testPrivateFieldAccess() {
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(TestMe.class, Integer.class, "i");
        TestMe t = new TestMe();
        updater.compareAndSet(t, null, 1);
    }
    private class TestMe {
        private volatile Integer i;
    }
}

java.lang.RuntimeException: java.lang.IllegalAccessException: Class atomic.AtomicTests can not access a member of class atomic.AtomicTests$TestMeField with modifiers “private volatile”
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.
(AtomicReferenceFieldUpdater.java:189)

The target object is passed null.

    public void testNulTargetObject() {
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(TestMe.class, Integer.class, "i");
        TestMe t = new TestMe();
        updater.compareAndSet(null, null, 1);
    }

java.lang.ClassCastException
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.updateCheck(AtomicReferenceFieldUpdater.java:218)

Field name is incorrect.

    public void testInvalidField() {
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(TestMe.class, Integer.class, "j");
    }

java.lang.RuntimeException: java.lang.NoSuchFieldException: j
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.<init>(AtomicReferenceFieldUpdater.java:189)

Field type is wrong.

    public void testFieldTypeWrong() {
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(TestMe.class, Double.class, "i");
    }

java.lang.ClassCastException
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.
(AtomicReferenceFieldUpdater.java:193)
Field is not volatile.

    
    public void testFieldIsNotVolatile() {
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(TestMe.class, Integer.class, "i");
    }
    private class TestMeField {
        Integer i;
    }

java.lang.IllegalArgumentException: Must be volatile type
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.(AtomicReferenceFieldUpdater.java:196)

If the target class and the caller class are not the same and the field is protected.

    public void testProtectedFieldAccess() {
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(TestMe.class, Integer.class, "i");
        TestMe t = new TestMe();
        updater.compareAndSet(t, null, 1);
    }
    private class TestMe {
        protected volatile Integer i;
    }

java.lang.RuntimeException: java.lang.IllegalAccessException: Class atomic.AtomicTests can not access a protected member of class atomic.AtomicTests$TestMe using an instance of atomic.AtomicTests$TestMe
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.ensureProtectedAccess(AtomicReferenceFieldUpdater.java:266)
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.updateCheck(AtomicReferenceFieldUpdater.java:220)
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.compareAndSet(AtomicReferenceFieldUpdater.java:227)

The target object passed is not an instance of updater’s target type.

    public void testTargetTypeWrong() {
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(TestMe.class, Integer.class, "i");
        AtomicTests a = new AtomicTests();
        updater.compareAndSet(a, null, 1);
    }

java.lang.ClassCastException
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.updateCheck(AtomicReferenceFieldUpdater.java:218)
at java.util.concurrent.atomic.AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl.compareAndSet(AtomicReferenceFieldUpdater.java:227)

Successful Update

The caller class is not of the same type as the target object but it is its subclass, field is protected. 

Even if the field is protected, it will still allow update as a subclass can access protected field.

    public void testTargetObjectInherited() {
        TestMeInherited t = new TestMeInherited();
        t.update(1);
        assertEquals(1, t.i.intValue());
    }
    private class TestMeInherited extends TestMe {
        AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(TestMe.class, Integer.class, "i");
        private void update(Integer inNewInt) {
            updater.compareAndSet(this, i, inNewInt);
        }
    }
    private class TestMe {
        protected volatile Integer i;
    }

Caller’s class

sun.reflect.Reflection.getCallerClass(3); returns the caller’s class. The first element is Reflection class itself. The second element is the class from where the Reflection.getCallerClass() is called and so on.

Suppose the class structure is:

public class AtomicTests extends TestCase {
    public void testClassTree() {
        TestUpdater t = new TestUpdater();
        t.update(2);
        assertEquals(2, t.i.intValue());
    }
}
public class TestUpdater {
    protected volatile Integer i;
    AtomicReferenceFieldUpdater updater = AtomicReferenceFieldUpdater.newUpdater(TestUpdater.class, Integer.class, "i");
    public void update(Integer inNewInt) {
        updater.compareAndSet(this, i, inNewInt);
    }
}
public abstract class AtomicReferenceFieldUpdater  {
    private static final class AtomicReferenceFieldUpdaterImpl
 extends AtomicReferenceFieldUpdater {
    }
}

sun.reflect.Reflection.getCallerClass()[0-5] returns:
Atomic Field Updaters

 

If we represent each class as a box, the inner most being the Reflection class itself, the above structure would look like:

 

Atomic Field Updaters

 

 

Share.

Leave A Reply