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.
1. decrementAndGet: 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.

Leave a Reply