Open In App

AtomicInteger for Lock Free Algorithms in Java

Last Updated : 14 Sep, 2022
Improve
Improve
Like Article
Like
Save
Share
Report

Lock-Free Algorithms is one of the mechanisms in which thread-safe access to shared data is possible without the use of Synchronization primitives like mutexes. Multi-threaded applications have shared resources that may be passed among different threads used in the application. 

  • This poses the threat of race conditions and data races among the threads. In order to handle this situation, various techniques are used. One of the most common ways is to use synchronization and locks(also called monitors) which ensures thread safety. However, If excessive synchronization is used, the application performance would be hugely impacted along with the application getting more and more complex.
     
  • We need to write applications that are thread-safe but at the same time, they should give us high performance and the benefit of concurrent execution. So, in order to minimize the usage of synchronization and reduce the complexity of locks, Java has a whole set of classes in the java.util.concurrent package which provides many advanced operations that are lock-free and atomic.
     
  • A large set of operations, in an application, can be performed without the need to synchronize most of the code. We need to understand what applies best in a situation and use the right tool for the job.
     
  • We will see an example where first we display how synchronized locking is used in multi-threaded applications to achieve concurrency, and then we will see a solution providing a lock-free solution of the same problem.

The following example shows how synchronization and the locking mechanisms are used as a solution for concurrency.
 

Java




// Java Program to demonstrate the
// Synchronization of threads
// using Locks
 
import java.io.*;
 
class GFG {
    public static void main(String[] args)
        throws InterruptedException
    {
        // Creating Obj for CountTrees Class
        CountTrees countTrees = new CountTrees();
         
        // Creating Obj for IncreaseTrees Class
        IncreaseTrees increaseTreesThread = new IncreaseTrees(countTrees);
         
        // Creating Obj for IncreaseConcrete Class
        IncreaseConcrete increaseConcreteThread
            = new IncreaseConcrete(countTrees);
         
        // Starting both Thread increaseTreesThread
        // And increaseConcreteThread by using start method.
        increaseTreesThread.start();
        increaseConcreteThread.start();
         
        // Join method Enable threads wait to complete
        increaseTreesThread.join();
        increaseConcreteThread.join();
 
        // To print the no. of trees by getting current
        // value by using countTrees Obj.
        System.out.println("Number of trees in your area ::"
                           + countTrees.getNumOfTrees());
    }
}
 
// Implementation of IncreaseTrees using Thread
class IncreaseTrees extends Thread {
 
    private CountTrees countTrees;
 
    IncreaseTrees(CountTrees countTrees)
    {
        this.countTrees = countTrees;
    }
 
    @Override
    public void run()
    {
        System.out.println("Planting trees in progress...");
        countTrees.plantTrees();
    }
}
 
// Implementation of IncreaseConcrete using Thread
class IncreaseConcrete extends Thread {
 
    private CountTrees countTrees;
 
    IncreaseConcrete(CountTrees countTrees)
    {
        this.countTrees = countTrees;
    }
 
    @Override
    public void run()
    {
        System.out.println("Concretization in progress...");
        countTrees.concretizing();
    }
}
 
// Synchronizing the shared resources
class CountTrees {
 
    private int trees = 10000;
 
    public synchronized void plantTrees()
    {
        for (int i = 0; i < 10000; i++)
            trees++;
    }
 
    public synchronized void concretizing()
    {
        for (int i = 0; i < 10000; i++)
            trees--;
    }
 
    public synchronized int getNumOfTrees()
    {
        return this.trees;
    }
}


Output:

Planting trees in progress...
Concretization in progress...
Number of trees in your area:: 10000

Now to convert the above example into a simple lock-free sample, we use the AtomicInteger class of Java which is part of the Concurrent package java.util.concurrent.atomic.AtomicInteger. It is a very useful class which can be easily used in concurrent applications as below : 

Java




// Java program to demonstrate to achieve concurrency
// with the help of AtomicInteger class which is used
// in application like atomically incremented the counter
 
// Lock Free program for achieving concurrency
import java.io.*;
 
// Java provides wrapper class to achieve atomic
// operations without the use of synchronization
// like java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicInteger;
 
class GFG {
 
    public static void main(String[] args)
        throws InterruptedException
    {
        // Creating Obj for CountTrees Class
        CountTrees countTrees = new CountTrees();
 
        // Creating Obj for IncreaseTrees Class
        IncreaseTrees increaseTreesThread
            = new IncreaseTrees(countTrees);
 
        // Creating Obj for IncreaseConcrete Class
        IncreaseConcrete increaseConcreteThread
            = new IncreaseConcrete(countTrees);
 
        // Starting both Thread increaseTreesThread
        // and increaseConcreteThread by using
        // start method.
        increaseTreesThread.start();
        increaseConcreteThread.start();
 
        // join method Enable threads wait to complete
        increaseTreesThread.join();
        increaseConcreteThread.join();
 
        // To print the no. of trees by getting current
        // value by using countTrees Obj.
        System.out.println("Number of trees in your area ::"
                           + countTrees.getNumOfTrees());
    }
}
 
// Implementation of IncreaseTrees class
class IncreaseTrees extends Thread {
 
    private CountTrees countTrees;
 
    IncreaseTrees(CountTrees countTrees)
    {
        this.countTrees = countTrees;
    }
 
    @Override public void run()
    {
        System.out.println("Planting trees in process...");
        countTrees.plantTrees();
    }
}
 
class IncreaseConcrete extends Thread {
 
    private CountTrees countTrees;
 
    IncreaseConcrete(CountTrees countTrees)
    {
        this.countTrees = countTrees;
    }
 
    @Override public void run()
    {
        System.out.println("Concretization in progress...");
        countTrees.concretizing();
    }
}
 
// Implementation of CountTrees Class
class CountTrees {
 
    // In java AtomicInteger Class provides operations on
    // underlying int value that can be read and
    // written atomically.
    private AtomicInteger trees = new AtomicInteger(10000);
 
    // Implementation of plantTrees method
    public void plantTrees()
    {
        // AtomicInteger class obj trees
        // atomically incremented the value.
        for (int i = 0; i < 10000; i++)
            trees.incrementAndGet();
    }
    // Implementation of concretizing method
    public void concretizing()
    {
        // AtomicInteger class obj trees
        // decremented the value.
        for (int i = 0; i < 10000; i++)
            trees.decrementAndGet();
    }
 
    public int getNumOfTrees()
    {
        // AtomicInteger class obj
        // trees Gets the current
        // value.
        return trees.get();
    }
}


Output: 

Concretization in progress...
Planting trees in process...
Number of trees in your area::10000

Summary and Key Takeaways :

  • The AtomicInteger class is a great tool that can be used in simple applications like concurrent counting and building simple readable code without the complexity of using a lock.
  • AtomicInteger should be used only when atomic operations are needed. Also, the race condition can still exist between two separate atomic operations.
  • The AtomicInteger class is on par and can sometimes be more efficient than a regular integer with a lock as protection.
  • If an application is only using a single thread, the regular integer is preferred.

References: AtomicInteger class in Java



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads