Get free ebooK with 50 must do coding Question for Product Based Companies solved
Fill the details & get ebook over email
Thank You!
We have sent the Ebook on 50 Must Do Coding Questions for Product Based Companies Solved over your email. All the best!

Synchronization in Java

Last Updated on October 16, 2023 by Ankit Kochar

Synchronization in Java is a critical concept, especially in the context of multi-threaded programming. In a multi-threaded environment, multiple threads can access and modify shared resources concurrently, leading to race conditions and unpredictable behavior. Mechanisms of Synchronization in Java provide a way to control access to shared resources, ensuring thread safety and preventing data corruption.

In this article, we will explore the topic of synchronization in Java in-depth. We’ll discuss the importance of synchronization, various synchronization techniques, and the Java keywords synchronized and volatile. Additionally, we’ll cover common synchronization classes and best practices for writing thread-safe Java code. Understanding synchronization is essential for building robust and reliable multi-threaded applications in Java.

What is Synchronization in Java?

Synchronization in Java is a mechanism used to control access to shared resources in a multi-threaded environment. The goal of synchronization is to ensure that only one thread can access a shared resource at a time, to prevent data corruption and inconsistent results caused by multiple threads accessing and modifying the same data simultaneously.

Java provides the synchronized keyword to implement synchronization. When a method or a block of code is marked as synchronized, it can only be accessed by one thread at a time. This is achieved by acquiring a lock on the object associated with the synchronized method or block.

Synchronization is an important concept in multi-threaded programming, as it ensures that shared data remains consistent and in a valid state, even in the presence of multiple threads accessing and modifying it simultaneously.

Types of Synchronization in Java

There are two types of synchronization in Java:

  • Method-Level Synchronization: In method-level synchronization, the entire method is marked as synchronized and can only be accessed by one thread at a time. This is achieved by acquiring a lock on the object associated with the method.
  • Block-Level Synchronization: In block-level synchronization, only a portion of the method is marked as synchronized. This is achieved by wrapping the portion of code that requires synchronization within a synchronized block and acquiring a lock on a specified object.

Both method-level and block-level synchronization can be used to control access to shared resources in a multi-threaded environment, but block-level synchronization provides more fine-grained control over the synchronization of a program. Block-level synchronization allows multiple threads to access different parts of a shared resource simultaneously, which can improve performance in certain cases.

Let us discuss both of these types of Synchronization in detail with the help of suitable examples.

Java Synchronized Method

In Java Programming Language, we can make any method as Synchronized Method by using the synchronized keyword. This is used to lock a particular object for any shared resource.

When a thread calls a java synchronized method, the lock for that object is automatically acquired and released when the thread completes its work.

Syntax for making Java Synchronized Method:

Acess_modifiers synchronized return_type method_name (Method_Parameters) {
    // Method Statements
}

Let us first see the problem that arises if there is no Synchronization.

class Table{  
    void printTable(int n){ 
        for(int i=1;i<=5;i++){  
    		System.out.println(n*i);  
     
    		try{  
    			Thread.sleep(400);  
    		}catch(Exception e){
    			System.out.println(e);
    		}  
        }  
    }  
}  
  
class MyThread1 extends Thread{  
    Table t;  
    MyThread1(Table t){  
        this.t=t;  
    }  

    public void run(){  
        t.printTable(5);  
    }  
}  

class MyThread2 extends Thread{  
    Table t;  
    MyThread2(Table t){  
        this.t=t;  
    }
    
    public void run(){  
        t.printTable(100);  
    }  
}  
  
class Main{  
    public static void main(String args[]){ 
        //shared object
        Table obj = new Table();  
        MyThread1 t1=new MyThread1(obj);  
        MyThread2 t2=new MyThread2(obj);  
        t1.start();  
        t2.start();  
    }  
}

Output:

5
100
10
200
15
300
20
400
25
500

Here, we have got inconsistent output, since the object of class “Table” is being modified by the two methods concurrently.

We can overcome this problem by using the synchronized method as shown in the code below:

class Table{  
    synchronized void printTable(int n){ 
        for(int i=1;i<=5;i++){  
    		System.out.println(n*i);  
     
    		try{  
    			Thread.sleep(400);  
    		}catch(Exception e){
    			System.out.println(e);
    		}  
        }  
    }  
}  
  
class MyThread1 extends Thread{  
    Table t;  
    MyThread1(Table t){  
        this.t=t;  
    }  

    public void run(){  
        t.printTable(5);  
    }  
}  

class MyThread2 extends Thread{  
    Table t;  
    MyThread2(Table t){  
        this.t=t;  
    }
    
    public void run(){  
        t.printTable(100);  
    }  
}  
  
class Main{  
    public static void main(String args[]){ 
        //shared object
        Table obj = new Table();  
        MyThread1 t1=new MyThread1(obj);  
        MyThread2 t2=new MyThread2(obj);  
        t1.start();  
        t2.start();  
    }  
}

Output:

5
10
15
20
25
100
200
300
400
500

Here, we have modified the method as “synchronized method”. This ensures that the object in use by one thread is not available for the second thread fro making changes.

Java Synchronized Block

The synchronized block can be used to perform synchronization on any method resource.

Suppose, we have 100 lines of code in our method but only want to synchronize 5 of them, we can use synchronized block. If we put all of the method’s codes in the synchronized block, it will work just like the synchronized method.

Syntax for writing Java Synchronized Block:

synchronized (object reference expression) {     
  //statements/methods    
} 

Let us see an example to use Java Synchronized Block.

class Table{  
    void printTable(int n){    
        synchronized(this){//synchronized block    
    		for(int i=1;i<=5;i++){    
    			System.out.println(n*i);    
      
    			try{    
    				Thread.sleep(400);    
    			}catch(Exception e){
    				System.out.println(e);
    			}    
    		}    
        }   // end of synchronized block 
    }//end of the method    
}    
  
class MyThread1 extends Thread{  
    Table t;  
    MyThread1(Table t){  
        this.t=t;  
    }  

    public void run(){  
        t.printTable(5);  
    }  
}  

class MyThread2 extends Thread{  
    Table t;  
    MyThread2(Table t){  
        this.t=t;  
    }
    
    public void run(){  
        t.printTable(100);  
    }  
}  
  
class Main{  
    public static void main(String args[]){ 
        //shared object
        Table obj = new Table();  
        MyThread1 t1=new MyThread1(obj);  
        MyThread2 t2=new MyThread2(obj);  
        t1.start();  
        t2.start();  
    }  
}

Output:

5
10
15
20
25
100
200
300
400
500

Here, instead of making the whole method synchronized, we have only made a particular block “synchronized”. The synchronized block also locks that particular block free from the updation by another thread.

Need for Synchronized Keyword in Java

The synchronized keyword in Java is used to control access to a shared resource in a multi-threaded environment. The need for synchronization arises because multiple threads can access and modify shared data simultaneously, leading to inconsistencies and data corruption.

The synchronized keyword is used to provide mutual exclusion, ensuring that only one thread can access the shared resource at a time. This ensures that the shared data remains consistent and in a valid state, even in a multi-threaded environment.

Additionally, java synchronized helps to prevent race conditions, and deadlocks, and improve performance in certain cases. It is used to ensure the proper functioning of multi-threaded programs and to ensure that shared data is not corrupted by multiple threads.

In short, the need for the synchronized keyword in Java arises from the requirement to control access to shared resources in a multi-threaded environment and to ensure that shared data remains consistent and in a valid state. Let’s see the advantages of synchronization in Java.

Advantages of Synchronization in Java

Some of the advantages of using Synchronization in Java include

  • Thread Safety: Synchronization in Java helps to achieve thread safety by ensuring that only one thread can access the shared resource at a time.
  • Consistency: Synchronization ensures that the shared data is consistent and remains in a valid state throughout the program.
  • Deadlock Prevention: By using synchronization, deadlocks can be prevented in a multi-threaded environment.
  • Avoid Race Conditions: Synchronization helps to avoid race conditions that can occur in a multi-threaded environment, where multiple threads try to access a shared resource simultaneously.
  • Improved Performance: Synchronization can also improve performance in certain cases, as it reduces the number of threads that are waiting for access to a shared.

As everyone knows that everything has its pros and cons. We have seen the advantages of synchronization in Java, now, let’s look into disadvantages of synchronization in Java.

Disadvantages of Synchronization in Java

Despite having several advantages, Synchronization in Java also have some disadvantages. These disadvantages are listed below:

  • Performance Overhead: Synchronization can increase the overhead of a program, as it requires the threads to wait for access to the shared resource, leading to a decrease in overall performance.
  • Deadlocks: Although synchronization can prevent deadlocks, it can also lead to deadlocks if not used correctly.
  • Starvation: Synchronization can lead to starvation, where a thread waiting for access to a shared resource is blocked indefinitely.
  • Reduced Concurrency: Synchronization reduces concurrency as it limits the number of threads that can access a shared resource simultaneously. This can lead to decreased overall program efficiency.

Conclusion
Synchronization in Java is a fundamental concept that plays a crucial role in ensuring the correctness and reliability of multi-threaded applications. It provides the means to coordinate and control access to shared resources, preventing race conditions and data corruption.

In this article, we’ve delved into the world of synchronization in Java, covering the importance of thread safety, synchronization techniques, and synchronization classes. We’ve discussed the use of the synchronized keyword for controlling access to critical sections of code and the volatile keyword for ensuring visibility of shared data.

As you continue to develop Java applications, remember that synchronization is a powerful tool but must be used judiciously. Overusing synchronization can lead to performance bottlenecks, so it’s essential to strike a balance between thread safety and performance optimization. By mastering the art of synchronization, you can build multithreaded Java applications that are not only efficient but also reliable.

FAQs Related to Synchronization in Java

Let us see some Frequently Asked Questions on Synchronization in Java.

1. What are some best practices for writing thread-safe Java code?
Best practices for writing thread-safe Java code include minimizing the use of synchronization when not needed, using thread-safe collections, and avoiding unnecessary shared state between threads.

2. What is the synchronized keyword in Java?
The synchronized keyword is used to create synchronized blocks or methods, ensuring that only one thread can execute the synchronized code at a time. It provides mutual exclusion and is a fundamental mechanism for achieving thread safety.

3. When should I use the volatile keyword in Java?
The volatile keyword is used to declare variables that are shared among threads. It ensures that changes to the variable are visible to all threads, preventing caching of the variable’s value.

4. What is a race condition in multi-threaded programming?
A race condition occurs when two or more threads access shared data concurrently, and the final outcome depends on the relative timing of their execution. Race conditions can lead to data corruption and unexpected behavior.

5. Are there any built-in synchronization classes in Java?
Yes, Java provides built-in synchronization classes like java.util.concurrent.locks.Lock and java.util.concurrent.atomic.Atomic* classes that offer more advanced synchronization mechanisms for complex scenarios.

Leave a Reply

Your email address will not be published. Required fields are marked *