Atomic classes

Atomic classes enclose primitive values into objects and provide atomic operations on them. We discussed race conditions and volatile variables. For example, if we have an int variable to be used as a counter and we want to assign a unique value to objects that we work with, we can increment the value and use the result as a unique ID. However, when multiple threads use the same code, we cannot be sure about the value we read after the increment. It may happen that another thread also incremented the value in the meantime. To avoid that, we will have to enclose the increment and the assignment of the incremented value to an object into a synchronized block. This can also be done using AtomicInteger.

If we have a variable of AtomicInteger, then calling incrementAndGet increments the value of int enclosed in the class and returns the incremented value. Why do it instead of using synchronized block? The first answer is that if the functionality is there in the JDK, then using it is less line than implementing it again. Developers maintaining the code you create are expected to know the JDK libraries but have to study your code, and this takes time and time is money.

The other reason is that these classes are highly optimized and, many times, they implement the features using platform specific native code that greatly over performs the version we can implement using synchronized blocks. Worrying about performance too early is not good, but parallel algorithms and synchronization between threads are usually used when performance is crucial; thus, there is a good chance that the performance of the code using the atomic classes is important.

In the java.util.concurrent.atomic package, there are several classes, AtomicInteger, AtomicBoolean, AtomicLong, and AtomicReference among them. They all provide methods that are specific to the encapsulated value.

The method, which is implemented by every atomic class, is compareAndSet. This is a conditional value-setting operation that has the following format:

boolean compareAndSet(expectedValue, updateValue);

When it is applied on an atomic class, it compares the actual value with the one expectedValue, and if they are the same, then it sets the value to updateValue. If the value was updated, the method returns true and it does all this in an atomic action.

You may ask the question that if this method is in all of these classes, why is there no Interface defining this method? The reason for this is that the argument types are different based on the encapsulated type, and these types are primitives. As primitives cannot be used as generic types, not even a generic interface can be defined. In case of AtomicXXXArray, the method has an extra first argument, which is the index of the array element handled in the call.

The variables encapsulated are handled the same way as volatile, as far as the reordering is concerned, but there are special methods that loosen the conditions a bit to be used when possible, and performance is key.

The general advice is to consider using atomic classes, if there is one usable, and you will find yourself creating a synchronized block for check-and-set, atomic increment, or addition operations.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.51.228