Open In App

Java Method and Block Synchronization

Last Updated : 11 Jan, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

In Java, Synchronization is very important in concurrent programming when multiple threads need to access shared resources. Java Synchronization can be applied to methods and blocks. Method synchronization in Java locks the entire method and Block synchronization locks only a specific section of the method.

Method vs Block Synchronization

Features

Method Synchronization

Block Synchronization

Scope

It synchronizes the entire method.

Synchronizes only block code.

Performance

This may lead to unnecessary synchronization for non-critical sections.

More efficiently, synchronizes only critical sections.

Lock

Locks the method object.

Locks the object or class specified in the block.

Flexibility

Less flexible, synchronize the entire method

More flexible, allows partial synchronization

Types of Synchronzation

Java is a multi-threaded language where threads run in parallel, and synchronization is required for shared resources to ensure only one thread accesses them at a time. Synchronization prevents corruption of a mutable object’s state when shared by multiple threads. Synchronization is needed when the Object is mutable. If the object is immutable or only read by threads, synchronization is unnecessary. Java programming language provides two synchronization idioms:

  • Methods synchronization
  • Statements synchronization (Block synchronization)

Method Synchronization

In Java, method synchronization is achieved by adding the synchronized keyword. Then the entire method is treated as a critical section. Ensuring that only one thread can execute that method at any given time. This approach locks the entire method, preventing other threads from accessing it until the first thread finishes its execution.

Example 1: Below is the code example which shows the issue when multiple threads access the getLine() method without synchronization.

Java
// Multiple threads are executing on the same object 
// at same time without synchronization
import java.io.*;
class Line
{
    // if multiple threads(trains) will try to
    // access this unsynchronized method,
    // then object's  state will be corrupted
    public void getLine()
    {
        for (int i = 0; i < 3; i++)
        {
            System.out.println(i);
            try
            {
                Thread.sleep(100);
            }
            catch (Exception e)
            {
                System.out.println(e);
            }
        }
    }
}

class Train extends Thread
{
    // shared object
    Line line;

    Train(Line line)
    {
      
        // Initialize shared object
        this.line = line;
    }

    @Override
    public void run()
    {
        // Access the getLine() method
        line.getLine();
    }
}

public class Geeks
{
    public static void main(String[] args)
    {
        // Shared Line object
        Line obj = new Line();

        // creating the threads that are
        // sharing the same Object
        Train t1 = new Train(obj);
        Train t2 = new Train(obj);

        // threads start their execution
        t1.start();
        t2.start();
    }
}

Output
0
0
1
1
2
2

Explanation: In the above example, two Train threads (t1 and t2) access the getLine() method of the shared Line object simultaneously. This results mixed output because the method is not synchronized and it allows both threads to execute it concurrently. This scenario creates a risk of data inconsistency or collision when accessing shared resources.

Example 2: The below example demonstrates how to synchronize access to the getLine() method to ensure that only one thread can execute it at a time.

Java
// Multiple threads execute the same method
// but in synchronized way
class Line
{   
    // Synchronized method ensures that only one thread
    // can execute this method at a time on the same object
    synchronized public void getLine()
    {
        for (int i = 0; i < 3; i++)
        {
            System.out.println(i);
            try
            {
                Thread.sleep(100);
            }
            catch (Exception e)
            {
                System.out.println(e);
            }
        }
    }
}

class Train extends Thread
{
    // Reference variable 
    Line line;

    Train(Line line)
    {
        this.line = line;
    }

    @Override
    public void run()
    {
        line.getLine();
    }
}

public class Geeks
{
    public static void main(String[] args)
    {
      
        // Object of Line class shared among the threads
        Line obj = new Line();
      
        // Creating threads that share the same object
        Train t1 = new Train(obj);
        Train t2 = new Train(obj);

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

Output
0
1
2
0
1
2

Explanation: In the above example, the getLine() method is declared as synchronized, which ensures that only one thread can execute it at a time on the same Line object. When t1 is running the method, t2 must wait for it to finish before accessing the method.

Block Synchronization

If we only need to execute some subsequent lines of code not all lines (instructions) of code within a method, then we should synchronize only block of the code within which required instructions are exists. For example, lets suppose there is a method that contains 100 lines of code but there are only 10 lines (one after one) of code which contain critical section of code i.e. these lines can modify (change) the Object’s state. So we only need to synchronize these 10 lines of code method to avoid any modification in state of the Object and to ensure that other threads can execute rest of the lines within the same method without any interruption.

Example:

Java
// Demonstration of Block Synchronization
import java.io.*;
import java.util.*;

class Geek {
    String name = "";
    public int count = 0;

    // Method where only a block is synchronized
    public void geekName(String geek, List<String> list) {
      
        // Only one thread is allowed to change 
        // the geek's name at a time
        synchronized(this) {
            name = geek;
          
            // Keep track of how many threads 
            // have changed the name
            count++;  
        }

        // Other threads are allowed to add geek's 
        // name to the list concurrently
        list.add(geek);
    }
}

class GFG {
    public static void main (String[] args) {
      
        Geek gk = new Geek();
        List<String> list = new ArrayList<String>();
        gk.geekName("Mohit", list);
        System.out.println(gk.name);
    }
}

Output
Mohit

Explanation: In this example, the geekName() method modifies the name variable and tracks how many times it has been changed using the count variable. The synchronized block ensures that only one thread can modify the name and update the count at a time. Adding names to the list is not synchronized, allowing multiple threads to add names concurrently without issues.

Important points:

  • When a thread enters into a synchronized method or block, it acquires the lock and once it completes its task and exits from the synchronized method, it releases the lock.
  • When ta hread enters into a synchronized instance method or block, it acquires Object level lock and when it enters into a synchronized static method or block it acquires class level lock.
  • Java synchronization will throw a null pointer exception if the Object used in the synchronized block is null. For example, If in synchronized(instance), the instance is null then it will throw a null pointer exception.
  • In Java, wait(), notify() and notifyAll() are the important methods that are used in synchronization.
  • You can not apply the Java synchronized keyword with the variables.
  • Don’t synchronize on the non-final field on the synchronized block because the reference to the non-final field may change anytime and then different threads might synchronize on different objects i.e. no synchronization at all.

Advantages

  • Multithreading: Since Java is a multithreaded language, synchronization is a good way to achieve mutual exclusion on a shared resource(s).
  • Instance and Static Methods: Both synchronized instance methods and synchronized static methods can be executed concurrently because they are used to lock different Objects.

Limitations

  • Concurrency Limitations: Java synchronization does not allow concurrent reads.
  • Decreases Efficiency: The Java synchronized method runs very slowly and can degrade the performance, so you should synchronize the method when it is absolutely necessary otherwise not synchronize the block only for critical sections of the code.


Next Article
Article Tags :
Practice Tags :

Similar Reads