AtomicReference and function methods

One of the new features in Hazelcast 3.2, which is going to be released in January, is the IAtomicReference. The IAtomicReference is the distributed version of the Java AtomicReference and can be created like this:

HazelcastInstance hz = ...
IAtomicReference<Float> ref = hz.getAtomicReference("foo");

This will create/load the IAtomicReference with the name ‘foo’.

We already had the IAtomicLong, but the IAtomicReference is more versatile since it can deal with all kinds of datatypes, e.g. String, Boolean, Double or your own objects.

Functions

The IAtomicReference contains most of the functionality the AtomicReference has like get/set/getAndSet/compareAndSet. But it also contains some new methods that accepts a function:

public interface Function<T,R> extends Serializable {
    apply(T input);
}

If we would have an AtomicReference of type Float, we could use the following function:

public class IncFunction implements Function<Float,Float>{
   public Float apply(Float input){
      if(input == null) return new Float(1);
      return input++;
   }
}

The IAtomicReference contains various methods to apply such a function:

  1. apply
  2. alter
  3. alterAndGet
  4. getAndAlter

These methods are described bellow:

Method ‘apply’

Applies the function to the value and sends back the result, without changing the stored value.

IAtomicReference<Float> ref = ..
ref.set(0);
Float result = ref.apply(new IncFunction());

The result will be 1, and but the stored value will remain 0.

Method ‘alter’

Applies the function to the value and updates the stored value with the result. No value is send back, so the return type of this method is void:

IAtomicReference<Float> ref = ..
ref.set(0);
ref.alter(new IncFunction());

After the alter has executed, the stored value will be 1. This method is practical if you don’t care for the calculated value since returning the value involves IO overhead.

Method ‘alterAndGet’

The ‘alterAndGet’ method is similar to the ‘alter’ method, but it will return the result of the calculation.

IAtomicReference<Float> ref = ..
ref.set(0);
Float result = ref.alterAndGet(new IncFunction());

After the ‘alterAndGet’ executed, the result and the stored value are 1.

Method ‘getAndAlter’

The ‘getAndAlter’ method is similar to the ‘alter’ and ‘getAndAlter’, but the big difference is that it will return the original value:

IAtomicReference<Float> ref = ..
ref.set(0);
Float result = ref.alterAndGet(new IncFunction());

After the ‘getAndAlter’ has executed, the stored value is 1, and the returned value is 0.

Advantages of Functions

You might wonder why not to implement the increment functionality like this:

IAtomicReference<Float> ref = ..
ref.set(ref.get()+1);

The problem is that this code has a race problem. So it could mean that you loose updates.

If you have more experience with the AtomicReference, you will probably come up with the following solution:

IAtomicReference<Float> ref = ..
for(;;){
   Float oldValue = ref.get();
   Float newValue = oldValue+1;
   if(ref.compareAndSet(oldValue,newValue)){
       break;
   }
}

Although this is correct and works fine on a non distributed environment, it involves IO operations in a distributed environment if the reference is not owned by the calling member. In a best case scenario there will be one for the ‘get’ and one for the ‘compareAndSet’.

It is better to send the function to the data, instead of pulling the data to the functionality because:

  1. It reduces the number of IO operations. And potentially reduces the amount of data going over the line if values are serialized to large blobs.
  2. There is no need for concurrency control. The function is executed on the partition thread, and therefor Hazelcast guarantees that the value stored in the reference, can’t be concurrently accessed by multiple threads. One thing to watch out for, is that the function should not take too much time, else you will be hogging the partition thread and this can lead to problems since other operations on that partition will be blocked. Perhaps that in the future we are going to include an option to execute the function on a different thread than the partition thread.

Functions need to be ‘serializable’

If the reference is not owned locally, the function needs to be send over the line to the remote machine. That is why the Function extends Serializable, so that is can be serialized. Luckily Hazelcast provides a very flexible serialization API where you can override the serialization behavior for a function.

In some cases you need to have access to resources within your Function; if you let the function implement HazelcastInstanceAware, the HazelcastInstance is injected and through that instance you can access your context.

IAtomicLong and functions

You might wonder about the IAtomicLong: could it also benefit from such functions? Yes it does; that is why we have ported the apply/alter/alterAndGet/getAndAlter functionality to the IAtomicLong as well.

Try it!

If you want to try out this new functionality, you can download the latest 3.2-SNAPSHOT from our snapshot repository by adding the following to your Maven pom:

<repositories>
   <repository>
     <id>snapshot-repository</id>
     <name>Maven2 Snapshot Repository</name>
        <url>https://oss.sonatype.org/content/repositories/snapshots</url>
   </repository>
</repositories>

Please let us know what you think of these new features!