Synchronization in Java
Hand holding gears

Introduction

Synchronization in java is the capability to control the access of multiple threads to any shared resource. In the Multithreading concept, multiple threads try to access the shared resources at a time to produce inconsistent results. The synchronization is necessary for reliable communication between threads.

Why we use Synchronization

  • Synchronization helps in preventing thread interference.
  • Synchronization helps to prevent concurrency problems.

Types of Synchronization

Synchronization is classified into two types

  • Process Synchronization
  • Thread Synchronization

Process Synchronization:

  • The process is nothing but a program under execution. It runs independently isolated from another process. The resources like memory and CPU time, etc. are allocated to the process by the operation System.

Thread Synchronization:

Thread synchronization is two types, they are:

1.Mutual Exclusive:

A Mutex or Mutual Exclusive helps only one thread to access the shared resources. It won’t allow the accessing of shared resources at a time. It can be achieved in the following ways.

  • Synchronized Method
  • Synchronized block
  • Static Synchronization

2. Cooperation (Inter Thread Communication in java)

Also check Java Tutorial for Beginners | An Overview of Java

Lock Concept in Java

  • Synchronization Mechanism developed by using the synchronized keyword in java language. It is built on top of the locking mechanism, this locking mechanism is taken care of by Java Virtual Machine (JVM). The synchronized keyword is only applicable for methods and blocks, it can’t apply to classes and variables. Synchronized keyword in java creates a block of code is known as a critical section. To enter into the critical section thread needs to obtain the corresponding object’s lock.

The problem without Synchronization:

Below example shows the Powers of the numbers like n1, n2, n3, n4, n5 

class Power{  
void printPower(int n){//method not synchronized
   int temp = 1;
   for(int i=1;i<=5;i++){ 
     System.out.println(Thread.currentThread().getName() + ":- " +n + "^"+ i + " value: " + n*temp);
     temp = n*temp;
     try{  
      Thread.sleep(500);  
     }catch(Exception e){System.out.println(e);}  
   }  
 }  
}  
class Thread1 extends Thread{  
Power p;  
Thread1(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(5);  
}    
}  
class Thread2 extends Thread{  
Power p;  
Thread2(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(8);  
}  
}  
  
public class Synchronization_Example1{  
public static void main(String args[]){  
Power obj = new Power();//only one object  
Thread1 p1=new Thread1(obj);  
Thread2 p2=new Thread2(obj);  
p1.start();  
p2.start();
}  
}

Output:

Thread-1:- 8^1 value: 8

Thread-0:- 5^1 value: 5

Thread-1:- 8^2 value: 64

Thread-0:- 5^2 value: 25

Thread-1:- 8^3 value: 512

Thread-0:- 5^3 value: 125

Thread-1:- 8^4 value: 4096

Thread-0:- 5^4 value: 625

Thread-1:- 8^5 value: 32768

Thread-0:- 5^5 value: 3125

Here we didn’t use the synchronized keyword so both the threads are executing at a time so in the output, thread-0 is interfering with thread-1, and hence, we are getting inconsistent results.

Java Synchronized Method

If we use the Synchronized keywords in any method then that method is Synchronized Method. 

  • It is used to lock an object for any shared resources. 
  • The object gets the lock when the synchronized method is called. 
  • The lock won’t be released until the thread completes its function.

Syntax:

Acess_modifiers synchronized return_type method_name (Method_Parameters) {

// Code of the Method.

}

Java Synchronized Method Example:

class Power{  
synchronized void printPower(int n){//method synchronized
   int temp = 1;
   for(int i=1;i<=5;i++){ 
        System.out.println(Thread.currentThread().getName() + ":- " +n + "^"+ i + " value: " + n*temp);
     temp = n*temp;
     try{  
      Thread.sleep(500);  
     }catch(Exception e){System.out.println(e);}  
   }  
 }  
}  
class Thread1 extends Thread{  
Power p;  
Thread1(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(5);  
}  
}  
class Thread2 extends Thread{  
Power p;  
Thread2(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(8);  
}  
}  
public class Synchronization_Example2{  
public static void main(String args[]){  
Power obj = new Power();//only one object  
Thread1 p1=new Thread1(obj);  
Thread2 p2=new Thread2(obj);  
p1.start();  
p2.start();
}  
}

Output:

Thread-0:- 5^1 value: 5

Thread-0:- 5^2 value: 25

Thread-0:- 5^3 value: 125

Thread-0:- 5^4 value: 625

Thread-0:- 5^5 value: 3125

Thread-1:- 8^1 value: 8

Thread-1: – 8^2 value: 64

Thread-1:- 8^3 value: 512

Thread-1:- 8^4 value: 4096

Thread-1:- 8^5 value: 32768

Here we used synchronized keywords. It helps to execute a single thread at a time. It is not allowing another thread to execute until the first one is completed, after completion of the first thread it allowed the second thread. Now we can see the output correctly the powers 5 and 8 from n1 to n5. Thread-0 completed then only thread-1 begin.

Synchronized Block

  • Suppose you don’t want to synchronize the entire method, you want to synchronize few lines of code in the method, then a synchronized block helps to synchronize those few lines of code. It will take the object as a parameter. It will work the same as Synchronized Method. In the case of synchronized method lock accessed is on the method but in the case of synchronized block lock accessed is on the object.

Syntax:

synchronized (object) {
//code of the block.
}
Program to understand the Synchronized Block:
class Power{  
void printPower(int n){ 
synchronized(this){ //synchronized block
   int temp = 1;
   for(int i=1;i<=5;i++){ 
        System.out.println(Thread.currentThread().getName() + ":- " +n + "^"+ i + " value: " + n*temp);
     temp = n*temp;
     try{  
      Thread.sleep(500);  
     }catch(Exception e){System.out.println(e);}  
   }  
 }  
}  
}
  
class Thread1 extends Thread{  
Power p;  
Thread1(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(5);  
}  
  
}  
class Thread2 extends Thread{  
Power p;  
Thread2(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(8);  
}  
}  
  
public class Synchronization_Example3{  
public static void main(String args[]){  
Power obj = new Power();//only one object  
Thread1 p1=new Thread1(obj);  
Thread2 p2=new Thread2(obj);  
p1.start();  
p2.start();

}  
}

Output:

Thread-0:- 5^1 value: 5

Thread-0:- 5^2 value: 25

Thread-0:- 5^3 value: 125

Thread-0:- 5^4 value: 625

Thread-0:- 5^5 value: 3125

Thread-1:- 8^1 value: 8

Thread-1:- 8^2 value: 64

Thread-1:- 8^3 value: 512

Thread-1:- 8^4 value: 4096

Thread-1:- 8^5 value: 32768

In this example, we didn’t synchronize the entire method but we synchronized few lines of code in the method. We got the results exactly as the synchronized method.

Static Synchronization

  • In java, every object has a single lock (monitor) associated with it. The thread which is entering into synchronized method or synchronized block will get that lock, all other threads which are remaining to use the shared resources have to wait for the completion of the first thread and release of the lock.
  • Suppose in the case of where we have more than one object, in this case, two separate threads will acquire the locks and enter into a synchronized block or synchronized method with a separate lock for each object at the same time. To avoid this, we will use static synchronization.
  • In this, we will place synchronized keywords before the static method. In static synchronization, lock access is on the class not on object and Method.

Syntax:

synchronized static return_type method_name (Parameters) {
//code
}
Or 
synchronized static return_type method_name (Class_name.class) {
//code
}

Program without Static Synchronization:
class Power{  
 synchronized void printPower(int n){ //static synchronized method
   int temp = 1;
   for(int i=1;i<=5;i++){ 
     System.out.println(Thread.currentThread().getName() + ":- " +n + "^"+ i + " value: " + n*temp);
     temp = n*temp;
     try{  
      Thread.sleep(400);  
     }catch(Exception e){}  
   }  
  
 }  
}    
class Thread1 extends Thread{  
Power p;  
Thread1(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(2);  
}  
  
}

class Thread2 extends Thread{  
Power p;  
Thread2(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(3);  
} 
}  

class Thread3 extends Thread{  
Power p;  
Thread3(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(5);  
}  
} 

class Thread4 extends Thread{ 
Power p;  
Thread4(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(8);  
}  
} 

public class Synchronization_Example4{  
public static void main(String args[]){ 
Power ob1 = new Power(); //first object
Power ob2 = new Power(); //second object
Thread1 p1 = new Thread1(ob1);  
Thread2 p2 = new Thread2(ob1); 
Thread3 p3 = new Thread3(ob2);
Thread4 p4 = new Thread4(ob2);

p1.start();  
p2.start();
p3.start();
p4.start();
}  
}

Output:

Thread-2:- 5^1 value: 5

Thread-0:- 2^1 value: 2

Thread-2:- 5^2 value: 25

Thread-0:- 2^2 value: 4

Thread-2:- 5^3 value: 125

Thread-0:- 2^3 value: 8

Thread-2:- 5^4 value: 625

Thread-0:- 2^4 value: 16

Thread-2: – 5^5 value: 3125

Thread-0: – 2^5 value: 32

Thread-3:- 8^1 value: 8

Thread-1:- 3^1 value: 3

Thread-3:- 8^2 value: 64

Thread-1:- 3^2 value: 9

Thread-3:- 8^3 value: 512

Thread-1:- 3^3 value: 27

Thread-3:- 8^4 value: 4096

Thread-1:- 3^4 value: 81

Thread-3:- 8^5 value: 32768

Thread-1:- 3^5 value: 243

If you observe the above results Thread-0, Thread-1 belongs to object-1 and Thread-2, Thread-3 are belonging to Object-2. So, there is no interference between thread 0 and 1 because of the same object (obj1). In the same way, there is no interference between Thread 2 and 3 because they belong to the same object (obj2). But if you observe there is interference between Thread 0 and 2, same as there is interference between Thread 1 and 3. To rectify this problem we will use static synchronization.

Program with static synchronization:

class Power{  
 synchronized static void printPower(int n){ //static synchronized method
   int temp = 1;
   for(int i=1;i<=5;i++){ 
     System.out.println(Thread.currentThread().getName() + ":- " +n + "^"+ i + " value: " + n*temp);
     temp = n*temp;
     try{  
      Thread.sleep(400);  
     }catch(Exception e){}  
   }  
  
 }  
}    
class Thread1 extends Thread{  
Power p;  
Thread1(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(2);  
}  
  
}

class Thread2 extends Thread{  
Power p;  
Thread2(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(3);  
} 
}  

class Thread3 extends Thread{  
Power p;  
Thread3(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(5);  
}  
} 

class Thread4 extends Thread{ 
Power p;  
Thread4(Power p){  
this.p=p;  
}  
public void run(){  
p.printPower(8);  
}  
} 

public class Synchronization_Example4{  
public static void main(String args[]){ 
Power ob1 = new Power(); //first object
Power ob2 = new Power(); //second object
Thread1 p1 = new Thread1(ob1);  
Thread2 p2 = new Thread2(ob1); 
Thread3 p3 = new Thread3(ob2);
Thread4 p4 = new Thread4(ob2);

p1.start();  
p2.start();
p3.start();
p4.start();
}  
}

Output:

Thread-0:- 2^1 value: 2

Thread-0:- 2^2 value: 4

Thread-0:- 2^3 value: 8

Thread-0:- 2^4 value: 16

Thread-0:- 2^5 value: 32

Thread-1:- 3^1 value: 3

Thread-1:- 3^2 value: 9

Thread-1:- 3^3 value: 27

Thread-1:- 3^4 value: 81

Thread-1:- 3^5 value: 243

Thread-2:- 5^1 value: 5

Thread-2:- 5^2 value: 25

Thread-2:- 5^3 value: 125

Thread-2:- 5^4 value: 625

Thread-2:- 5^5 value: 3125

Thread-3:- 8^1 value: 8

Thread-3:- 8^2 value: 64

Thread-3:- 8^3 value: 512

Thread-3:- 8^4 value: 4096

Thread-3:- 8^5 value: 32768

In this static synchronization, we can observe there is no interference between Thread-0 and Thread-2 same as there is no interference between Thread-1 and 3. The next thread is executing after the previous thread completion or releasing lock only.

Inter – Thread Communication

Inter – Thread communication or cooperation is a communication of two or more threads with each other. It can be done by using the following methods.

  • wait()
  • notify()
  • notifyAll()

Why we need Inter – Thread Communication?

  • There is a situation on the thread that keeps on checking some conditions repeatedly, once that condition satisfies thread moves with the appropriate action. This situation is known as polling. This is a wastage of CPU time, to reduce the wastage of CPU time due to polling, java uses Inter – Thread Communication Mechanism.
  • wait(), notify(), notifyAll() methods must be called within a synchronized method or block otherwise program will compile but when you run it, it will throw illegal monitor State Exception.

Example:

class Power{  
void printPower(int n){
   int temp = 1;
   for(int i=1;i<=5;i++){ 
     System.out.println(Thread.currentThread().getName() + ":- " +n + "^"+ i + " value: " + n*temp);
     temp = n*temp;
     try{  
        this.wait();    //wait placed outside of the synchronized block or method
      Thread.sleep(500);  
     }catch(Exception e){System.out.println(e);}  
   }  
  
 }  
}  

 Output:

Thread-0:- 5^1 value: 5

java.lang.IllegalMonitorStateException

Thread-0:- 5^2 value: 25

java.lang.IllegalMonitorStateException

Thread-0:- 5^3 value: 125

java.lang.IllegalMonitorStateException

Thread-0:- 5^4 value: 625

java.lang.IllegalMonitorStateException

Thread-0:- 5^5 value: 3125

java.lang.IllegalMonitorStateException

Thread-1:- 8^1 value: 8

java.lang.IllegalMonitorStateException

Thread-1:- 8^2 value: 64

java.lang.IllegalMonitorStateException

Thread-1:- 8^3 value: 512

java.lang.IllegalMonitorStateException

Thread-1:- 8^4 value: 4096

java.lang.IllegalMonitorStateException

Thread-1:- 8^5 value: 32768

java.lang.IllegalMonitorStateException

  1. wait () Method
  • It causes the current thread to place itself into the waiting stage until another thread invokes the notify() method or notifyAll() method for this object.
  1. notify () Method
  • This method wakes up a single thread called wait () on the same object. If there is more than one thread that is waiting on this same object, then any one of them arbitrarily chosen to be awakened. Here awakened thread will not able to proceed until the current thread release lock. If any threads are trying to get the lock on this object then the awakened thread will also compete with them in the usual manner.

Syntax:

public final void notify()

  1. notify All() Method
  • Rather than a single thread, it will wake up all the threads waiting on this object monitor. The awakened thread will not able to proceed until the current thread releases the lock. Again, these awakened threads need to compete with all other threads which are trying to get the lock on this object.

Syntax:

public final void notifyAll()

The Drawback of Synchronization Mechanism 

Synchronization Mechanism shows less performance.
Let’s consider an example, if there are five process P1, P2, P3, P4, P5 that are waiting to get the shared resources to access only one thread at a time so, all other processes are in waiting condition, the last process has to wait until all other processes to be complete. So, we have to use the synchronization concept where we will get inconsistent results.

If you are interested in learning more about Java visit Great Learning Academy.

0

LEAVE A REPLY

Please enter your comment!
Please enter your name here

16 − 8 =