Java SE 8 For the Really Impatient, Note 9
1,660 words in 10 minutes
Chapter 6 Concurrency Enhancements
java.util.concurrent is a mix of useful utilities for the application programmer and power tools for library authors, without much effort to separate the two.
Key points:
- Updating atomic variables has become simpler with the
updateAndGet/accumulateAndGetmethods. LongAccumulator/DoubleAccumulatorare more efficient thanAtomicLong/AtomicDoubleunder high contention.- Updating entries in a
ConcurrentHashMaphas become simpler with thecomputeandmergemethods. ConcurrentHashMapnow has bulk operationssearch,reduce,forEach, with variants operating on keys, values, keys and values, and entries.- A set view lets you use a
ConcurrentHashMapasSet. - The
Arraysclass has methods for parallel sorting, filling, and prefix operations. - Completable futures let you compose asynchronous operations.
Atomic Values
java.util.concurrent.atomic package provided classes for lock-free mutation of variables since Java 5. You can safely generate a sequence of numbers like below:
incrementAndGet: atomically increments the AtomicLong and returns the post-increment value.
If you want to make a more complex update, you have to use the compareAndSet method. Suppose you want to keep track of the largest value that is observed by different thread.
Instead, compute the new value and use compareAndSet in a loop.
If another thread is also updating largest, it is possible the it has beat this thread to it. Then compareAndSet will return false without setting the new value. The loop tries again. Eventually, it will succeed replacing the existing value with the new one. The compareAndSet method maps to a processor operation that is faster than using a lock.
In Java 8, you can use a lambda expression.
The accumulateAndGet method takes a binary operator that is used to combine the atomic value and the supplied argument.
Also see getAndUpdate and getAndAccumulate the return the old value.
These methods are also provided for:
AtomicIntegerAtomicIntegerArrayAtomicIntegerFieldUpdaterAtomicLongArrayAtomicLongFieldUpdaterAtomicReferenceAtomicReferenceArrayAtomicReferenceFieldUpdater
LongAdder and LongAccumulator can be used to solve the problem that a large number of threads accessing the same atomic values.
LongAdder: composed of multiple variables whose collective sum is the current value. Multiple threads can update different summands, and new summands are automatically provided when the number of threads increases. Efficient when the value of the sum is not needed until after all work has been done. If you anticipate high contention, you should simply use a LongAdder instead of an AtomicLong. Call increment to increment a counter, or add to add a quantity, and sum to retrieve the total.
|
|
increment does not return the old value which would undo the efficiency gain.
LongAccumulator: generalizes the idea to an arbitrary accumulation operaiton. Provide the operation and its neutral element in the constructor. Call accumulate to incorporate new values. Call get to obtain the current value.
Internally, the accumulator has variables a1, a2, …, an. Each variable is initialized with the neutral element.
When accumulate is called with value v, then one of them is atomically updated as ai = ai op v, where op is the accumulation operation written in infix form. In the above example, a call to accumulate computes ai = ai + v for some i.
The result of get is a1 op a2 op … op an.
If you choose a different operation, you can compute maximum or minimum.
The operation must be associative and commutative, meaning that the final result must be independent of the order.
DoubleAdder and DoubleAccumulator work in the same way with double values.
StampedLock: can be used to implement optimistic reads. Not recommended to use locks.
First call tryOptimisticRead, upon which you get a “stamp”. Read your values and check whether the stamp is still valid(no other thread has obtained a write lock). If so, you can use the values. If not, get a read lock (which blocks any writers).

