What is meant by atomic
Atomic means a single unit. In terms of execution, an if an operation consists of multiple steps then it is said to be atomic when all the steps are executed as a single unit.
That is, either all the steps will be executed or none if any one fails.
Scenario
Suppose we have a multithreaded program in which 2 threads access a single integer value and each thread increments it by 10.
Learn how to create threads here.
If the integer is initialized to 0, then practically the value of the integer variable after both threads have executed should be equal to 20.
Example,
class Task implements Runnable { // integer accessed by multiple thread int counter; @Override public void run() { try { // loop from 1 to 10 for (int j = 1; j <= 10; j++) { // pause current thread Thread.sleep(1000); // increment value by 1 count++; } } catch(Exception e) { e.printStackTrace(); } } } public class MultiThreaded { public static void main(String[] a) { // create a task Task obj = new Task(barrier); // create three worker threads Thread t1 = new Thread(obj); Thread t2 = new Thread(obj); // start threads t1.start(); t2.start(); try { // wait for threads to finish t1.join(); t2.join(); // print counter value System.out.println("Value of counter is: " + obj.counter); } catch (Exception e) { e.printStackTrace(); } } }
When the above program is executed multiple times, following are the outputs generated.
Value of counter is: 18
Value of counter is: 20
Value of counter is: 19
As evident from the output, the value of counter after both the threads finish is random.
This is because when one thread goes to sleep inside the loop, other thread increments the value of counter.
When the sleeping thread resumes, it has the old value(or sometimes updated value too) of counter and it increments the wrong value.
In other words, both the threads do not have an updated value of the shared variable.
There are following ways to solve this problem
- Use locks by wrapping the code that increments the counter into a synchronized block.
- Make the counter as volatile so that each thread receives its updated copy.
But using this approach affects performance and is a tedious task. This is where AtomicInteger
comes handy.
Java AtomicInteger
java.util.concurrent.atomic.AtomicInteger
is used in multithreaded applications where multiple threads need to access a shared integer.
It is a part of concurrent api in java and thus, it belongs to the concurrent package.AtomicInteger
contains an integer value that is updated atomically.
By atomically, it means that operations of reading, increment and updating the value are executed as a single operation and if any one of these fails, then the value is not updated.
This also means that all the threads always work on the updated value of the integer value.AtomicInteger
is thread safe meaning it can be shared across multiple threads as stated earlier. Also, it does not use any locking on the value.
Javadocs of java.util.concurrent.atomic
package state
A small toolkit of classes that support lock-free thread-safe programming on single variables.
How to use AtomicInteger
This section will explain how to create an AtomicInteger
, increment and decrease its value with examples and explanation.
Creating AtomicInteger
For using AtomicInteger
, we need to create its object. Its object can be created using its constructors as shown below.
AtomicInteger aiZero = new AtomicInteger(); // no-arg constructor AtomicInteger ai = new AtomicInteger(5); // 1-arg constructor
First declaration creates an AtomicInteger
with a initial value of 0(no argument supplied). Second creates an AtomicInteger
with an initial value of 5.
It is also possible to change the value of AtomicInteger
after creating it using its methods explained later.
Increment the value of AtomicInteger
Following methods are used to increment the value of an AtomicInteger
1. incrementAndGet
Increments the value by 1 and returns the updated value.
2. getAndIncrement
Retrieves the current value, increases it by 1 and updates it. This method returns the previous value of AtomicInteger
.
3. getAndAdd
This method takes an integer as argument and adds it to the value of AtomicInteger
. Before adding, it retrieves the current value and returns the older value after updating.
4. addAndGet
This method takes an integer as argument and adds it to the value of AtomicInteger
. It adds the value to the current value and returns the updated value.
Example,
// create atomicinteger with initial value of 5 AtomicInteger ai = new AtomicInteger(); ai.incrementAndGet(); // returns 6 ai.getAndIncrement(); // returns 6 but value is now 7 ai.getAndAdd(4); // returns 7 but value is now 11 ai.addAndGet(4); // returns 15
Decrement the value of AtomicInteger
Similar to increment methods, there are following methods that reduce the value of AtomicInteger
.
Reduces the value of AtomicInteger
by 1 and returns the updated value.
2. getAndDecrement: Retrieves the current value, decreases it by 1 and updates it. This method returns the previous value of AtomicInteger
.
// create atomicinteger with initial value of 5 AtomicInteger ai = new AtomicInteger(); ai.decrementAndGet(); // returns 4 ai.getAndDecrement(); // returns 4 but value is now 3
Getting the value of AtomicInteger
For getting the value of AtomicInteger
, use its get
method. Example,
// create atomicinteger with initial value of 5 AtomicInteger ai = new AtomicInteger(); ai.get(); // returns 5
Setting the value of AtomicInteger
Following method is used to set the value of AtomicInteger
.
set: Takes an integer argument and updates the value of AtomicInteger
to supplied value.
Example,
// create atomicinteger with initial value of 5 AtomicInteger ai = new AtomicInteger(); ai.set(10); // sets value to 10
Compare and change value of AtomicInteger
Often you need to change the value of AtomicInteger
only if its current value is some expected value. There are two methods to accomplish this.
1. compareAndSet
It takes 2 arguments where the first argument is the expected value and second is the value to set.
If the current value of AtomicInteger
is equal to the expected value, then its current value is updated with the value in the second argument.
This method will return true
if the operation was successful, that is, if the expected value was equal to the current value, false
otherwise.
2. compareAndExchange
This method also behaves in the same manner as compareAndSet()
except that instead of returning a boolean, it returns the older or expected value irrespective of the operation being successful or unsuccessful.
Example,
// create atomicinteger with initial value of 5 AtomicInteger ai = new AtomicInteger(); ai.set(10); // sets value to 10 ai.compareAndSet(5, 20); // returns false since values do not match ai.compareAndSet(10, 20); // returns true and sets the value to 20 ai.compareAndExchange(5, 20); // returns 20, value is 20 ai.compareAndExchange(20, 30); // returns 20, value becomes 30
If you found this post useful, press the clap button below to appreciate.