Open In App

Synchronization in Java

Last Updated : 30 Oct, 2025
Comments
Improve
Suggest changes
205 Likes
Like
Report

Synchronization in Java is a mechanism that ensures that only one thread can access a shared resource (like a variable, object, or method) at a time. It prevents concurrent threads from interfering with each other while modifying shared data.

Why is Synchronization Needed?

  • Prevents Data Inconsistency: Ensures that multiple threads don’t corrupt shared data when accessing it simultaneously.
  • Avoids Race Conditions: Allows only one thread to execute a critical section at a time, maintaining predictable results.
  • Maintains Thread Safety: Protects shared resources from concurrent modification by multiple threads.
  • Ensures Data Integrity: Keeps shared data accurate and consistent throughout program execution.

Ways to Achieve Synchronization

synchronizatin
Synchronization

1. Synchronized Methods

A synchronized method ensures that only one thread can execute it at a time on the same object instance

Java
class Counter{
    
    // Shared variable
    private int c = 0; 

    // Synchronized method to increment counter
    public synchronized void inc(){
        c++; 
        
    }

    // Synchronized method to get counter value
    public synchronized int get(){
        return c; 
        
    }
}

public class Geeks{
    
    public static void main(String[] args){
        
        // Shared resource
        Counter cnt = new Counter(); 

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++)
                cnt.inc();
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++)
                cnt.inc();
        });

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Counter: " + cnt.get());
    }
}

Output
Counter: 2000

Explanation: Both threads increment the same counter concurrently. Since the inc() and get() methods are synchronized, only one thread can access them at a time, ensuring the correct final count.

2. Synchronized Blocks

Instead of synchronizing an entire method, Java allows synchronization on specific blocks of code. This improves performance by locking only the necessary section.

Java
class Counter{

    private int c = 0;

    public void inc(){

        // Synchronize only this block
        synchronized (this) { c++; }
    }

    public int get() { return c; }
}

public class Geeks {

    public static void main(String[] args)
        throws InterruptedException{
            
        Counter cnt = new Counter();

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++)
                cnt.inc();
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++)
                cnt.inc();
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("Counter: " + cnt.get());
    }
}

Output
Counter: 2000

Explanation: The synchronized block ensures mutual exclusion only for the increment statement, reducing the locking overhead.

3. Static Synchronization

Static synchronization is used to synchronize static methods. In this case, the lock is placed on the class object rather than the instance.

Java
class Table{
    
    synchronized static void printTable(int n){
        
        for (int i = 1; i <=3; i++){
            
            System.out.println(n * i);
            try {
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }
}

class Thread1 extends Thread{
    
    public void run() {
        Table.printTable(1);
    }
}

class Thread2 extends Thread {
    public void run() {
        Table.printTable(10);
    }
}

public class GFG{
    
    public static void main(String[] args){
        
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        t1.start();
        t2.start();
    }
}

Output
1
2
3
10
20
30

Explanation: Both threads t1 and t2 call the static synchronized method printTable(). The lock is applied to the Table.class object, ensuring that only one thread can access the method at a time, even if no object instance is shared.

Types of Synchronization

There are two type of synchronizations in Java which are listed below:

1. Process Synchronization

Process Synchronization is a technique used to coordinate the execution of multiple processes. It ensures that the shared resources are safe and in order.

Java
class BankAccount{

    // Shared resource (bank balance)
    private int balance = 1000;

    // Synchronized method for deposit operation
    public synchronized void deposit(int amount){
        
        balance += amount;
        System.out.println("Deposited: " + amount
                           + ", Balance: " + balance);
    }

    // Synchronized method for withdrawal operation
    public synchronized void withdraw(int amount){
        
        if (balance >= amount) {
            balance -= amount;
            System.out.println("Withdrawn: " + amount
                               + ", Balance: " + balance);
        }
        else {
            System.out.println(
                "Insufficient balance to withdraw: "
                + amount);
        }
    }

    public int getBalance() { return balance; }
}

// Main class
public class Geeks{
    
    public static void main(String[] args){
        
        BankAccount account
            = new BankAccount(); // Shared resource

        // Thread 1 to deposit money into the account
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                account.deposit(200);
                try {
                    Thread.sleep(50); // Simulate some delay
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // Thread 2 to withdraw money from the account
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                account.withdraw(100);
                try {
                    Thread.sleep(
                        100); // Simulate some delay
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // Start both threads
        t1.start();
        t2.start();

        // Wait for threads to finish
        try {
            t1.join();
            t2.join();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }

        // Print final balance
        System.out.println("Final Balance: "
                           + account.getBalance());
    }
}

Output
Deposited: 200, Balance: 1200
Withdrawn: 100, Balance: 1100
Deposited: 200, Balance: 1300
Deposited: 200, Balance: 1500
Withdrawn: 100, Balance: 1400
Withdrawn: 100, Balance: 1300
Final Balance: 1300

Explanation: Two threads perform deposit and withdrawal operations simultaneously. The synchronized methods prevent race conditions, ensuring consistent balance updates.

2. Thread Synchronization in Java

Thread Synchronization is used to coordinate and ordering of the execution of the threads in a multi-threaded program. There are two types of thread synchronization are mentioned below:

Example: Ticket Booking System

Java
class TicketBooking{
    
    // Shared resource (available tickets)
    private int availableTickets
        = 10; 

    // Synchronized method for booking tickets
    public synchronized void bookTicket(int tickets){
        
        if (availableTickets >= tickets){
            
            availableTickets -= tickets;
            System.out.println(
                "Booked " + tickets
                + " tickets, Remaining tickets: "
                + availableTickets);
        }
        else{
            System.out.println(
                "Not enough tickets available to book "
                + tickets);
        }
    }

    public int getAvailableTickets(){
        
        return availableTickets;
    }
}

public class Geeks{
    
    public static void main(String[] args){
        
        // Shared resource
        TicketBooking booking
            = new TicketBooking(); 

        // Thread 1 to book tickets
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 2; i++) {
                
                // Trying to book 2
                // tickets each time
                booking.bookTicket(2); 
                                       
                try{
                    
                    // Simulate delay
                    Thread.sleep(50); 
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // Thread 2 to book tickets
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 2; i++){
                
                // Trying to book 3
                // tickets each time
                booking.bookTicket(3); 
                                       
                try{
                    
                    // Simulate delay
                    Thread.sleep(40);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // Start both threads
        t1.start();
        t2.start();

        // Wait for threads to finish
        try {
            t1.join();
            t2.join();
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }

        // Print final remaining tickets
        System.out.println("Final Available Tickets: "
                           + booking.getAvailableTickets());
    }
}

Output
Booked 2 tickets, Remaining tickets: 8
Booked 3 tickets, Remaining tickets: 5
Booked 3 tickets, Remaining tickets: 2
Booked 2 tickets, Remaining tickets: 0
Final Available Tickets: 0

Explanation: The synchronized bookTicket() method ensures that only one thread books tickets at a time, preventing overbooking and ensuring correct availability.

Volatile Keyword

The volatile keyword in Java ensures that all threads have a consistent view of a variable's value. It prevents caching of the variable's value by threads, ensuring that updates to the variable are immediately visible to other threads.

Working of Volatile Modifier:

  • It applies only to variables.
  • volatile guarantees visibility i.e. any write to a volatile variable is immediately visible to other threads.
  • It does not guarantee atomicity, meaning operations like count++ (read-modify-write operations) can still result in inconsistent values
Java
class Counter {
    private volatile boolean running = true;

    public void stop() {
        running = false;
    }

    public void start() {
        new Thread(() -> {
            while (running) {
                System.out.println("Running...");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            System.out.println("Stopped.");
        }).start();
    }
}

public class Geeks {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        counter.start();

        Thread.sleep(600); // Let it run briefly
        counter.stop();    // Then stop the thread
    }
}

Output
Running...
Running...
Running...
Stopped.

Explanation: The volatile variable running ensures that updates made by one thread (in stop()) are visible to the thread running the loop in start().

Volatile vs Synchronized

Features

Synchronized

Volatile

Applies to

It applies only to blocks or methods.

It applies to variables only.

Purpose

It ensures mutual exclusion and visibility

It ensures visibility of changes to variables across threads

Performance

Performance is relatively low compared to volatile because of the acquisition and release of the lock.

Performance is relatively high compared to synchronized Keyword.


Article Tags :

Explore