Another advanced threading feature introduced in Tiger is that of atomic type. An atomic operation is one that is indivisible: no other threads can interrupt or examine a variable in the middle of an atomic operation. There's the beginning state, the end state, and (for all other threads) nothing in between. An atomic type is simply a type that has atomic operations available to itit manages to be thread-safe despite being essentially lock-free.
All atomic types are defined in the java.util.concurrent.atomic package. There are a number of types, revolving around Boolean, Long, Integer, and object references. This allows you to perform atomic operations on these types, using AtomicBoolean, AtomicLong, AtomicInteger, and AtomicReference, respectively.
Each type provides a get( ) and set( ) method, which do what you would expect (get and set the type's value, using an atomic operation). They also offer getAndSet( ), which sets the value, returning the previous value, as well as compareAndSet( ), which checks the value, and if it matches the supplied value, sets it to a new value. Additionally, AtomicInteger and AtomicLong provide for atomic versions of ++ and --, through variations on decrement( ) and increment( ) methods. For example, decrementAndGet( ) decrements the value of the atomic type, and returns the update value; getAndIncrement( ) returns the current value, and then increments it in the type. Here are several different ways to write a thread-safe counter, lifted straight out of Java in a Nutshell, Fifth Edition (O'Reilly):
NOTE
There are some variations on these types, with additional features, that you can check out in the Tiger Javadocs.
// Rely on locking to prevent concurrent access int count1 = 0; public synchronized int count1( ) { return count1++; } // Rely on the atomic operations to prevent concurrent access AtomicInteger count2 = new AtomicInteger(0); public int count2( ) { return count2.getAndIncrement( ); } // Optimistic locking -- compare the result, to minimize overhead, // and only correct if needed AtomicInteger count3 = new AtomicInteger(0); public int count3( ) { int result; do { result = count3.get( ); } while (!count3.compareAndSet(result, result+1)); return result; }
If you're not familiar with object references, it's simply the reference to an object. AtomicReference, then, allows you to work with an object atomically, by getting and setting the reference in an indivisible manner. The most useful method on AtomicReference is probably compareAndSet( ), which lets you change an object reference if it doesn't match the supplied value.
NOTE
"compareAndSet" is the canonical atomic operation.
Like the lab "Advanced Synchronizing," getting too much further into atomic types would have us well into the ground covered by Java Threads (O'Reilly), so I'll refer you to that work if you need to get further detail on atomic types.
...types like byte, short, and char? These (and their wrapper types) can all be stored in an AtomicInteger, providing you the same functionality. You'll just have to do a little conversion on the object's return values, which are almost always an int.
You can also use these atomic operations on arrays of the Integer, Long, and reference types (but not Boolean). java.util.concurrent.atomic defines AtomicIntegerArray, AtomicLongArray, and AtomicReferenceArray for just these occasions. They provide all the methods of their non-array counterparts, but each method takes an int index to indicate which item in the array you want to operate upon.