Thread Safety vs Non-thread Safety

Last Updated : 18 Jul, 2025

When multiple threads work on the same data and the value of that data changes, the scenario is not thread-safe, resulting in inconsistent results. When a thread is already working on an object and preventing another thread from working on the same object, this process is called Thread Safety. This article explores what thread safety is, how it differs from non-thread-safe code, and techniques to make your Java applications thread-safe.

What is Thread Safety?

A thread-safe class or method ensures correct behavior and data consistency when accessed by multiple threads concurrently. For example, when a thread is already working on an object and preventing another thread from working on the same object, this process is called Thread Safety.

How to Achieve Thread Safety in Java?

There are four ways to achieve Thread Safety in Java. These are:

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

Examples of Thread-Safe Classes in Java

These classes handle synchronization internally to ensure safe access by multiple threads.

Code Example: Thread Safe using Synchronization

Java
// Thread-safe example using synchronized method
class Calculator {
    // Synchronized method to ensure thread safety
    synchronized void addNumbers(int base)
    {
        Thread current = Thread.currentThread();
        for (int i = 1; i <= 5; i++) {
            System.out.println(current.getName() + " : "
                               + (base + i));
        }
    }
}

// Worker class extending Thread to perform operation
class WorkerThread extends Thread {
    // Shared Calculator instance
    Calculator calc = new Calculator();

    @Override public void run()
    {
        // Calling the synchronized method
        calc.addNumbers(10);
    }
}

// Main class to run the thread-safe example
public class Geeks {
    public static void main(String[] args)
    {
        // Creating an instance of WorkerThread
        WorkerThread worker = new WorkerThread();

        // Creating two threads that share the same
        // WorkerThread instance
        Thread threadOne = new Thread(worker);
        Thread threadTwo = new Thread(worker);

        // Naming the threads for clarity
        threadOne.setName("Alpha");
        threadTwo.setName("Beta");

        // Starting both threads
        threadOne.start();
        threadTwo.start();
    }
}

Explanation:

  • Calculator class has a synchronized method addNumbers() to ensure thread-safe access.
  • WorkerThread class extends Thread and creates its own Calculator instance.
  • In run(), it calls addNumbers(10) using its Calculator instance.
  • In main(), one WorkerThread object is shared by two threads (Alpha and Beta).
  • Both threads start and call addNumbers(10) through the same WorkerThread.
  • Problem: Each thread still uses its own Calculator, so synchronization is ineffective.
  • Output may appear interleaved because threads are not actually sharing the same resource.

What is Non-Thread Safety?

Non-thread-safe class or method does not provide the correct answer when multiple threads execute simultaneously. When multiple threads access and modify shared data without proper synchronization, it can lead to:

  • Data races
  • Inconsistent state
  • Unexpected behavior
  • Crashes or corruption

Examples of Non-Thread-Safe Classes in Java

These classes do not handle synchronization internally and can lead to data inconsistency in multithreaded environments.

Code Example: Non-Thread Safe

Java
// Non-thread-safe example without synchronized method
class Calculator {
    // Method without synchronization - not thread-safe
    void addNumbers(int base)
    {
        Thread current = Thread.currentThread();
        for (int i = 1; i <= 5; i++) {
            System.out.println(current.getName()+" : "+(base + i));
        }
    }
}
// Worker class extending Thread to perform operation
class WorkerThread extends Thread {
    // Shared Calculator instance passed via constructor
    Calculator calc;

    WorkerThread(Calculator calc) { this.calc = calc; }

    @Override public void run()
    {
        // Calling the non-synchronized method
        calc.addNumbers(10);
    }
}
// Main class to run the non-thread-safe example
public class Geeks {
    public static void main(String[] args)
    {
        // Single shared Calculator object
        Calculator sharedCalc = new Calculator();

        // Creating two threads with the same Calculator
        // instance
        WorkerThread threadOne = new WorkerThread(sharedCalc);
        WorkerThread threadTwo = new WorkerThread(sharedCalc);

        // Naming the threads
        threadOne.setName("Alpha");
        threadTwo.setName("Beta");

        // Starting both threads
        threadOne.start();
        threadTwo.start();
    }
}

Explanation:

  • The calculator has a non-thread-safe addNumbers() method.
  • Two threads share the same Calculator object.
  • Both threads call addNumbers(10) simultaneously.
  • Since there’s no synchronization, output may be interleaved or inconsistent

Comparison table of Thread Safety and Non-thread Safety

Features

Thread-Safe

Non-Thread Safe

Definition

It is used to handle concurrent access by multiple threads safely

Not designed for safe concurrent access by multiple threads

Synchronization

Uses synchronization internally for Thread safety

It does not use synchronization; access must be manually managed.

Use Case

It is used in multi-threaded environments.

It is mainly used for single-threaded scenarios or with external sync.

Performance

Slower due to synchronization overhead.


Faster, as there’s no locking mechanism.


Scalability

May not scale well under high concurrency due to lock contention

Scales well if used in a controlled single-threaded context


Examples

Vector, Hashtable, ConcurrentHashMap, StringBuffer

ArrayList, HashMap, StringBuilder, SimpleDateFormat

Comment