Table of contents
1.
Introduction
2.
What is Java Thread Safe?
3.
Why do we Need Thread safe in Java?
3.1.
Multithreading
3.2.
Race Condition in Multithreading
4.
Thread Safety Techniques in Java
5.
How to Achieve Thread Safety in Java?
5.1.
1. Using Final Keyword
5.2.
Java
5.3.
2. Using Volatile Keyword
5.4.
Java
5.5.
3. Using Synchronization
5.6.
Java
5.7.
4. Using Atomic Variable
5.8.
Java
6.
Thread Safe Classes in Java
7.
Important Points About Thread-Safety in Java
8.
Frequently Asked Questions
8.1.
Why StringBuffer is thread-safe?
8.2.
How can we achieve thread safe in Java?
8.3.
What is thread-safe and non-thread-safe?
8.4.
Which set is thread-safe in Java?
8.5.
What does thread-safe mean in Java?
9.
Conclusion
Last Updated: Oct 28, 2024
Medium

Thread Safe in Java

Author Jay Dhoot
1 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

In today’s multi-threaded programming environments, ensuring thread safety is essential for building robust and efficient applications. Java, known for its strong support for concurrency, offers multiple tools and practices to handle threads safely. However, threads can lead to issues like data inconsistency, race conditions, and unexpected behavior without proper management. This blog dives into the concept of thread safety in Java, exploring what makes a piece of code thread-safe.

Thread Safe in JAVA

Thread safe in Java is a way to prevent data inconsistency by preventing another thread from working on the same object while a thread is working on it. In this article, we will learn about thread-safe in Java. We will also discuss achieving thread-safe in Java and thread-safe classes in Java.  

What is Java Thread Safe?

Preventing another thread from working on the same object while a thread is working on it is called Thread Safe in Java. A data structure is termed thread-safe when multiple threads can perform operations concurrently without involving the risk of race conditions. 

When a thread performs some action on a particular object, no other thread should be allowed to delete or modify the object.

Also see,  Eclipse ide for Java Developers

Why do we Need Thread safe in Java?

Before diving into thread safety in Java, Let’s first understand Multithreading and Race condition in Multithreading.

Multithreading

Java supports Multithreading, meaning that multithreaded programs can be developed using Java. Multithreading is the process of executing multiple threads concurrently or simultaneously.

A thread is the fundamental/smallest unit of processing. Threads are lightweight sub-processes that use a shared memory area. Several threads can run within a single program.

In simple words, a multithreaded program has two or more parts(called ‘threads’) that can run simultaneously and perform different tasks at the same time.

Race Condition in Multithreading

As multiple threads use the same data resources, it might be possible that one thread interrupts another thread’s read operation for its own delete or update operation. 

A race condition occurs when two or more threads access the critical section. A critical section is that part of a program where shared memory is accessed.
 

Consider a scenario where two threads update the same integer variable, ‘counter.’

Race Condition

Clearly, both threads read the value, decrement it and then update the variable counter. But as we can see, Thread C could access the data value before Thread N updates the counter variable, leading to inconsistent results. We expected two separate decrements by both the threads, i.e., the final value of the counter variable was expected to be 0, but instead, we got it as 1. This scenario is a typical example of race condition.

Thready Safe in Java is needed to prevent race conditions as it leads to inconsistency because of unsyncronization of multiple threads executing at the same time.

Thread Safety Techniques in Java

  1. Mutexes: Mutexes (mutual exclusion locks) are used to protect shared resources from being accessed concurrently by multiple threads. They ensure that only one thread can access the resource at a time, preventing race conditions.
  2. Semaphores: Semaphores are signaling mechanisms used to control access to a common resource by multiple threads. They are useful for managing a fixed number of resources and can be used to signal the availability of a resource.
  3. Atomic Operations: Atomic operations are indivisible operations that are executed without interference from other threads. They ensure that a thread can perform a read-modify-write sequence on a shared variable without interruption.
  4. Thread-Local Storage: Thread-local storage provides each thread with its own separate instance of a variable. This technique avoids the need for synchronization since each thread has its own copy of the data.
  5. Locks: Locks are synchronization primitives that provide a mechanism for threads to coordinate access to shared resources. There are various types of locks, including read-write locks, which allow multiple readers but only one writer.
  6. Condition Variables: Condition variables are used in conjunction with mutexes to allow threads to wait for certain conditions to be met before proceeding. They enable threads to sleep until a specific condition is signaled by another thread.

How to Achieve Thread Safety in Java?

There are many ways to achieve Thread Safety in Java:

  1. Using Final Keyword
  2. Using Volatile Keyword
  3. Using Synchronization
  4. Using Atomic Variable

1. Using Final Keyword

Final variables are thread-safe as once they are assigned with some reference of an object, they can not modify or point to the reference of another object. For example,

Implementation

  • Java

Java

public class Main
{
   // Variable int of type final
   final int c = 1;
  
   void method()
   {
       //Will throw an error as value of
       //final Variable can not be changed once assigned
       c = 5;
   }
}
You can also try this code with Online Java Compiler
Run Code


Output

Compilation Failed
Solution.java:1: error: class Main is public, should be declared in a file named Main.java
public class Main
       ^
Solution.java:10: error: cannot assign a value to final variable c
        c = 5;
        ^
2 errors

2. Using Volatile Keyword

It is a field modifier that allows multiple threads to use the instance and method of the classes simultaneously without any problem. It doesn't cache the variable's value and always reads it from the main memory. For example,

Implementation

  • Java

Java

public class Main
{
   // Creating a volatile variable count
   static volatile int count=0;

   // Static method
   static void increment()
   {
       count=count+1;
   }

   // Static method
   static void show()
   {
       System.out.println(
           "count=" + count);
   }
  
public static void main(String[] args)
{
//Creating Thread a
Thread a = new Thread()
{
           public void run()
           {
               for (int j = 0; j < 3; j++)
               {
                   increment();
               }
           }
       };

       // Creating Thread b
       Thread b = new Thread()
       {
           public void run()
           {
               for (int j = 0; j < 3; j++)
               {
                  show();
               }
           }
       };

       // Executing the Threads simultaneously
       a.start();
       b.start();
}
}
You can also try this code with Online Java Compiler
Run Code


Output

count=3
count=3
count=3

3. Using Synchronization

Synchronization allows only one thread at a time to complete a particular task. Java provides a method of synchronizing its task using synchronized blocks, which synchronize on some objects. It uses the synchronized keyword. All synchronized blocks are synchronized on the same object, with only one thread running inside them at a time. These synchronized blocks of code are called a critical section which prevents all other threads from attempting to enter inside it until the thread inside the synchronized block is done with its execution and has left the critical section. For example,

Implementation

  • Java

Java

class C {
   synchronized void sync(int n)
   {

       // Creating a thread instance
       Thread t = Thread.currentThread();
       String s = new String(t.getName());
       System.out.println("Only "+s+ " is inside synchronized block");
       for (int i = 1; i <= 5; i++) {
           System.out.println(s + " : " + (n*i));
       }
      
       System.out.println("Execution of "+s+ " is complete");
       System.out.println();
   }
}


class N extends Thread {

   C a = new C();
   public void run()
   {

       // Calling sync method
       a.sync(1);
   }
}
public class Main
{
   public static void main(String[] args)
   {

       // Creating an object of class N
       N b = new N();

       // Initializing Thread s1
       Thread s1 = new Thread(b);

       // Initializing Thread s2
       Thread s2 = new Thread(b);

       // Setting names of Thread
       s1.setName("Thread Coding");
       s2.setName("Thread Ninjas");

       // Executing the Threads simultaneously
       s1.start();
       s2.start();
   }
}
You can also try this code with Online Java Compiler
Run Code


Output

Only Thread Coding is inside synchronized block
Thread Coding : 1
Thread Coding : 2
Thread Coding : 3
Thread Coding : 4
Thread Coding : 5
Execution of Thread Coding is complete

Only Thread Ninjas is inside synchronized block
Thread Ninjas : 1
Thread Ninjas : 2
Thread Ninjas : 3
Thread Ninjas : 4
Thread Ninjas : 5
Execution of Thread Ninjas is complete

Also see, Java Ioexception

4. Using Atomic Variable

Atomic variables ensure that when multiple threads share the same variable, only one thread can access and modify the variable at a time. For example,

Implementation

  • Java

Java

import java.util.concurrent.atomic.AtomicInteger;
class Count {

   // Variable of class type Atomic Integer
   AtomicInteger num= new AtomicInteger();

   // Increment Method
   public void increment()
   {
       num.addAndGet(1);
   }
}

public class Main
{
   public static void main(String[] args) throws Exception
   {

       // Creating an object of class Count
       Count c = new Count();

       // Creating Thread a
       Thread a = new Thread
       (
           new Runnable()
           {
               public void run()
               {
                   for (int i = 1; i <= 5; i++) {
                       c.increment();
                   }
               }
           }
       );

       // Creating Thread b
       Thread b = new Thread
       (
           new Runnable()
           {
               public void run()
               {
                   for (int i = 1; i <= 5; i++) {
                       c.increment();
                   }
               }
           }
       );

       // Executing the Threads simultaneously
       a.start();
       b.start();

       // Calling join method on Thread a and Thread b
       a.join();
       b.join();

       System.out.println("Value of num after updation by both threads is " +c.num);
   }
}
You can also try this code with Online Java Compiler
Run Code


Output

Value of num after updation by both threads is 10

Thread Safe Classes in Java

Thread Safe Classes are designed so that multiple threads can use them concurrently without causing data inconsistency, data races, and other concurrency-related issues. These classes guarantee that their internal structure is maintained consistently and all operations on the class objects are properly synchronized and executed atomically.

Some of the thread-safe classes in Java are HashtableStackVector, etc.

Important Points About Thread-Safety in Java

Thread safety in Java is necessary to prevent conflicts when multiple threads access shared resources concurrently. 

  • Concurrent Access: When several threads access data or objects simultaneously, it can lead to issues like data corruption or unexpected behavior.
     
  • Immutable Objects: Immutable objects, once created, cannot be changed. They are inherently thread-safe because they eliminate the need for synchronization.
     
  • For safe concurrent operations, Java offers thread-safe classes like ConcurrentHashMap and AtomicInteger.
     
  • Volatile Keyword: The volatile keyword ensures the visibility of variable changes across threads, enhancing thread safety.
     
  • Avoiding Race Conditions: Identifying and avoiding race conditions is crucial to maintain thread safety.
     
  • Deadlocks: Careful coding is essential to prevent deadlocks, where threads wait indefinitely for each other.
     
  • Concurrency Utilities: Java's java.util.concurrent package provides high-level concurrency utilities for more accessible thread-safe programming.
    Read more, how to run java program

Frequently Asked Questions

Why StringBuffer is thread-safe?

StringBuffer is thread-safe because it's designed to handle multiple threads safely. It locks parts of its operations to prevent conflicts when different threads access it simultaneously.

How can we achieve thread safe in Java?

Thread safe in Java can be achieved by using final keyword, using volatile keyword, using synchronization and using atomic variables.

What is thread-safe and non-thread-safe?

Thread-safe code ensures consistent results when accessed by multiple threads simultaneously, avoiding data corruption or race conditions. Non-thread-safe code lacks such protections, potentially causing conflicts when multiple threads access shared data concurrently.

Which set is thread-safe in Java?

Java’s CopyOnWriteArraySet and ConcurrentSkipListSet are thread-safe sets, allowing safe access by multiple threads without needing external synchronization. These sets provide consistent performance in concurrent applications by handling internal synchronization automatically.

What does thread-safe mean in Java?

In Java, thread-safe means that a class or method can be accessed by multiple threads at once without causing data inconsistencies or unexpected behavior, often achieved through synchronization or using Java’s concurrency utilities.

Conclusion

In this article, we have discussed thread-safe in Java. Thread safety is a crucial aspect of building reliable, high-performing Java applications in a multi-threaded environment. By understanding what makes code thread-safe, developers can prevent issues like race conditions and data inconsistencies. Java offers powerful concurrency tools, such as synchronized collections, locks, and atomic variables, that simplify creating thread-safe applications.

Recommended articles:

We hope this article has helped you understand thread safe in Java. If this article helped you in any way, then you can read more such articles on our platform, Code360. You will find articles on almost every topic on our platform. For interview preparations, you can read the Interview Experiences of popular companies.

Live masterclass