Overview

In this article, we will understand what is AtomicInteger in java, its use, creation, working, commonly used methods with examples and their explanations.

Suppose there is an integer value and it is being incremented by multiple threads at a time as shown in the below example.

Note: For understanding AtomicInteger, you do not need to have the knowledge of threads in java.

class CountHolder implements Runnable {

  // integer variable
  int counter;
  @Override
  public void run() {
    for (int i = 0; i < 5; i++) {
      try {
        // pause the thread
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      // increment integer variable
      ++counter;
    }
  }
  
}
public class Incrementor {
  
  public static void main(String[] args) throws InterruptedException {
    CountHolder t = new CountHolder();
    // create two threads
    Thread t1 = new Thread(t);
    Thread t2 = new Thread(t);
    // start threads
    t1.start();
    t2.start();
    // wait for the threads to finish
    t1.join();
    t2.join();
    System.out.println("Value of counter is: " +t.counter);
  }
}

Above code example has a class that implements Runnable interface and provides the definition of run() method.
It also contains an integer which is incremented inside run() method.
Note that inside run() method, each thread is made to sleep. This represents a time taking task being executed by the thread.
There is another class that has the main() method which creates two threads and starts them. Note that both threads share the same instance of the class that has run() method.

When the program is executed, what should be the value of integer variable.
10, right?
No, it will fluctuate between 5-9 but never be 10.
Reason
Updating an integer comprises of following operations:
1. Reading the current value of integer,
2. Incrementing or modifying it, and
3. Setting its value to the new value.

All these are performed as different tasks or these are not atomic. So, while one thread is reading the value, other thread might be incrementing it and another thread might be setting its value.
This will definitely lead to incorrect final value.

To solve this, all the three tasks should be performed as a single operation or Atomic operation.
This means that while one thread is acting on a variable, no other thread could access it.

This could be achieved with thread synchronization using synchronized keyword but synchronization is a performance overhead. There is a more elegant solution.

AtomicInteger
java.util.concurrent.atomic.AtomicInteger is used to define an integer value and perform atomic operations on it.
AtomicInteger ensures that modification of integer variable happens as a single operation.

This is particularly useful in multi-threaded environment but it can also be used for updating a variable inside a lambda expression since you can only access final variables inside a lambda expression.
AtomicInteger was added in Java 5 but some new methods were added to it in java 8 as well.
Creating AtomicInteger
An AtomicInteger can be created using its constructor. It has two constructors:

1. Parameterized constructor which takes an integer argument.
This argument is its default value. Example,

// create atomic integer with value of 10
AtomicInteger ai = new AtomicInteger(10);

2. No-argument constructor. Initial value is 0.

// create atomic integer with value of 0
AtomicInteger ai = new AtomicInteger();

AtomicInteger methods
Following are the commonly used methods of an AtomicInteger.

MethodDescription
get()Returns the current value of AtomicInteger.
set(int value)Sets the value of AtomicInteger to the given value.
getAndSet(int value)Sets the value of AtomicInteger to the given value and returns the older value.
compareAndSet(int old, int new)Set the value of AtomicInteger to new if its present value is the same as old.
Returns true if old matches the present value.
compareAndExchange(int old, int new)Set the value of AtomicInteger to new if its present value is the same as old.
Returns the earlier value  if old matches the present value.
This method was added in java 9.
getAndIncrement()Increments the present value by 1 and returns the older value.
getAndDecrement()Decreases the present value by 1 ad returns the older value.
getAndAdd(int value)Adds the given value to the present value and returns the older value.
incrementAndGet()Increases the present value by 1 and returns the new value.
decrementAndGet()Decreases the present value by 1 and returns the new value.
addAndGet(int value)Adds the given value to the present value and returns the new value.
getAndUpdate(IntUnaryOperator u)Applies the given function over the present value and updates it. Returns the older value.

IntUnaryOperator is a functional interface having applyAsInt() method that takes a single argument and returns an integer.
This method was added to AtomicInteger in java 8.

updateAndGet(IntUnaryOperator u)Similar to above but returns the new value.
This method was added to AtomicInteger in java 8.
intValue()Similar to get()
longValue()Returns the value converted to long.
floatValue()Returns the value converted to float.
doubleValue()Returns the value converted to double.

All the methods defined above perform their task atomically. Thus, these are thread safe operations.
AtomicInteger Example
Below is an example program to demonstrate the working of AtomicInteger.

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerDemo {
   public static void main(String[] args) {
     // create a new object
     AtomicInteger ai = new AtomicInteger();
     System.out.println("Initial value: " +ai.get());
     // update value
     ai.set(10);
     System.out.println("New value: "+ai.get());
     // increase and decrease by 1
     System.out.println("Increment by 1. New value: "+ai.incrementAndGet());
     System.out.println("Decrement by 1. New value: "+ai.decrementAndGet());

     // modify value if it matches the given value
     System.out.println("----------------");
     System.out.println("Compare and exchange...");
     ai.compareAndExchange(10, 50);
     System.out.println("Present value: "+ai.get());
     // no effect since existing value is not 20
     ai.compareAndExchange(20, 100);
     System.out.println("Present value: "+ai.get());
     // add 10 to present value
     System.out.println("----------------");
     System.out.println("Adding to present value...");
     int old = ai.getAndAdd(10);
     System.out.println("Old value: " +old);
     System.out.println("Preset value: " +ai.get());
     System.out.println("Modified value: " +ai.get());
     System.out.println("Adding 10 to present value again");
     int updated = ai.addAndGet(10);
     System.out.println("Updated value: " +updated);
  }
}

This prints

Initial value: 0
New value: 10
Increment by 1. New value: 11
Decrement by 1. New value: 10
—————-
Compare and exchange…
Present value: 50
Present value: 50
—————-
Adding to present value…
Old value: 50
Preset value: 60
Modified value: 60
Adding 10 to present value again
Updated value: 70

Output will be easy to understand if you go through the method descriptions explained earlier.

AtomicInteger Multi-thread example
At the start of this article, I showed an example where two threads increment the value of a shared variable and its result was not as expected.
The same example modified to use AtomicInteger in place of a plain integer is given below.

import java.util.concurrent.atomic.AtomicInteger;

class CountHolder implements Runnable {

  // define AtomicInteger variable
  AtomicInteger counter = new AtomicInteger(); 
  @Override
  public void run() {
    for (int i = 0; i < 5; i++) {
      try {
        // pause the thread
        Thread.sleep(500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      // increment integer variable
      counter.getAndIncrement();
    }
  }
  
}
public class Incrementor {
  
  public static void main(String[] args) throws InterruptedException {
    CountHolder t = new CountHolder();
    // create two threads
    Thread t1 = new Thread(t);
    Thread t2 = new Thread(t);
    // start threads
    t1.start();
    t2.start();
    // wait for the threads to finish
    t1.join();
    t2.join();
    System.out.println("Value of counter is: " +t.counter.get());
  }
}

Output of this program will be

Value of counter is: 10

Run it any number of times and with any number of threads without worrying about synchronization.

Hope the article was useful.