java.util.concurrent.atomic.AtomicLongArray Example
In this example we shall show you how to make use of AtomicLongArray
class, AtomicLongArray
is a long
array in which elements may be updated atomically. In a multithreading concurrent application architecture, we have a few tasks which are parallelized in such a way as to require no coordination between threads. The true problem comes when we need a coordination between those threads. For example, consider a thread pool, where the tasks being executed are generally independent of each other. If the thread pool feeds off a common work queue, then the process of removing elements from or adding elements to the work queue must be thread-safe, and that means coordinating access to the head, tail, or inter-node link pointers. And it is this coordination that causes all the trouble.
1. What is the best solution?
1.1. Locking:
Synchronization
is the traditional way to coordinate access to shared fields in the Java language where we assured that whichever thread holds the lock that protects a given set of variables will have exclusive access to those variables, and changes to those variables will become visible to other threads when they subsequently acquire the lock. However, Synchronization
comes with cost of performance where threads frequently ask to acquire the lock when it is already held by another thread.
1.2. Volatile:
Volatile variables can also be used to store shared variables at a lower cost than that of Synchronization
, but they have limitations. While writes to volatile variables are guaranteed to be immediately visible to other threads, there is no way to render a read-modify-write sequence of operations atomic because of accessing a volatile variable never holds a lock, it is not suitable for cases where we want to read-update-write as an atomic operation (unless we’re prepared to “miss an update”).
The Synchronization
(Locking) guarantees visibility and atomicity with a performance cost and Volatile guarantees visibility and not the atomicity. So, what is the best solution?
1.3. Atomic classes:
With the release of Java SE 5, Sun included a java.util.concurrent.atomic
package that addresses this limitation. And specifically they added these classes (AtomicInteger
; AtomicLong
; AtomicReference
; AtomicBoolean
; array forms of atomic integer; long; reference; and atomic marked reference and stamped reference classes, which atomically update a pair of values).
Let’s see AtomicLongArray
as an example of those classes.
2. Example:
In this example, we will use two method of AtomicLongArray
(getAndIncrement(int i)
, getAndDecrement(int i)
) to update the given AtomicLongArray
.
IncrementUpdateTask
class is a Runnable
task which increment each element in the given AtomicLongArray
by one.
2.1. IncrementUpdateTask.java:
package com.jcg; import java.util.concurrent.atomic.AtomicLongArray; /** * @author ashraf * */ public class IncrementUpdateTask implements Runnable { private AtomicLongArray atomicLongArray; public IncrementUpdateTask(AtomicLongArray atomicLongArray) { super(); this.atomicLongArray = atomicLongArray; } public void run() { try { for (int i = 0; i < atomicLongArray.length(); i++) { System.out.println("Increment element "+ i +" by 1"); atomicLongArray.getAndIncrement(i); Thread.sleep(1000); } } catch (InterruptedException ie) { ie.printStackTrace(); } finally { System.out.println("Increment task was done !!!"); } } }
DecrementUpdateTask
class is a Runnable
task which decrement each element in the given AtomicLongArray
by one.
2.2. DecrementUpdateTask.java:
package com.jcg; import java.util.concurrent.atomic.AtomicLongArray; /** * @author ashraf * */ public class DecrementUpdateTask implements Runnable { private AtomicLongArray atomicLongArray; public DecrementUpdateTask(AtomicLongArray atomicLongArray) { super(); this.atomicLongArray = atomicLongArray; } public void run() { try { for (int i = 0; i < atomicLongArray.length(); i++) { System.out.println("Decrement element" + i +" by 1"); atomicLongArray.getAndDecrement(i); Thread.sleep(1000); } } catch (InterruptedException ie) { ie.printStackTrace(); } finally { System.out.println("Decrement task was done !!!"); } } }
AtomicLongArrayDemo
class creates a new AtomicLongArray
with the predefined long array of 10 elements, then it creates a new ExecutorService
with 2 thread, After that it creates two Future
tasks to increment and decrement AtomicLongArray
at the same time.
2.3. AtomicLongArrayDemo.java:
package com.jcg; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLongArray; /** * @author ashraf * */ public class AtomicLongArrayDemo { private static final int ARRAY_SIZE = 10; /** * @param args */ public static void main(String[] args) { // Create a new long array of 10 element long[] longArray = new long[ARRAY_SIZE]; for (int i = 0; i < ARRAY_SIZE; i++) { longArray[i] = i + 1; } // Create a new AtomicLongArray with the predefined long array AtomicLongArray atomicLongArray = new AtomicLongArray(longArray); System.out.println("atomicLongArray before running tasks:\n" + atomicLongArray); System.out.println("Start running increment/decrement tasks ..."); // Create a new ExecutorService with 2 thread to Increment and Decrement // AtomicLongArray ExecutorService executor = Executors.newFixedThreadPool(2); // Start AtomicLongArray increment task Future futureIncrementTask = executor .submit(new IncrementUpdateTask(atomicLongArray)); // Start AtomicLongArray Decrement task Future futureDecrementTask = executor .submit(new DecrementUpdateTask(atomicLongArray)); while (true) { if (futureIncrementTask.isDone() && futureDecrementTask.isDone()) { System.out .println("Finish running increment/decrement tasks !!!"); System.out.println("atomicLongArray after running tasks:\n" + atomicLongArray); executor.shutdown(); break; } } } }
As we notice that the AtomicLongArray
wasn’t changed after we running the increment and decrement tasks because the AtomicLongArray
manages the updating process very well where each thread update is visible and atomic to the other thread. So, whenever the increment task update an element, this update immediately becomes visible and atomic to the decrement task which reverse this update.
2.4. Output:
atomicLongArray before running tasks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Start running increment/decrement tasks ... Increment element 0 by 1 Decrement element0 by 1 Increment element 1 by 1 Decrement element1 by 1 Increment element 2 by 1 Decrement element2 by 1 Increment element 3 by 1 Decrement element3 by 1 Increment element 4 by 1 Decrement element4 by 1 Increment element 5 by 1 Decrement element5 by 1 Increment element 6 by 1 Decrement element6 by 1 Increment element 7 by 1 Decrement element7 by 1 Increment element 8 by 1 Decrement element8 by 1 Increment element 9 by 1 Decrement element9 by 1 Increment task was done !!! Decrement task was done !!! Finish running increment/decrement tasks !!! atomicLongArray after running tasks: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
3. Download the Source Code of this example:
This was an example of how to use AtomicLongArray
.
You can download the full source code of this example here: AtomicLongArrayExampleCode.zip