Synchronization in Java

Synchronization in Java can be thought of as a queue at a ticketing counter where a number of customers are waiting for the current customer to finish. As soon as the current customer gets their ticket, all the other customers move one position closer to the ticket counter.

Synchronization deals with threads in the same way as a ticketing counter. Threads allow multiple processes to be conducted in multiple ways.

Suppose a process adds and multiplies a particular array of elements – two threads can be used to implement this, one can be used to multiply, and the other can perform addition. This allows less CPU usage and saves a lot of CPU time.

Synchronization, in particular, is a concept that ensures that exactly one thread is entertained at a time (i.e., multiple threads do not function at the same time​).

Why do we need synchronization?

Synchronization allows a programmer to perform concurrent programming and prevents data corruption. It can be better understood by the following example:

int a = 5; 
int b = 4; 
int c = 0;
c = a + b; 
c = c * a; 

Suppose that two threads exist. Thread one performs c = a + b while thread two performs c = c * a;.

Initially c=0, so what if thread two gets the cpu time before thread one? The value of c will remain zero despite the multiplication with a. The original answer should have been 9=5+4 and then 45=9*5 (i.e., c=45). However, if thread two occurs before thread one, the answer would be 0=0*5 and then 9=5+4 (i.e., c=9)​.

svg viewer

How it works

Synchronization in Java could be simplified by adding the Synchronized keyword, which is placed before defining a method. The two codes ahead will represent how synchronization works.

Code without synchronization

The output of this code will be in random order. This will be​ fixed (later) with synchronization.

class Array{
void printNumbers(int n){//method not synchronized
for(int i=1;i<=3;i++){
System.out.println(n*i);
try{
Thread.sleep(500); //delayed a thread by 500 ms
}
catch(Exception e){System.out.println(e);}
}
}
}
class MyThread1 extends Thread
{
Array a;
MyThread1(Array a){
this.a=a;
}
public void run(){
a.printNumbers(1);
}
}
class MyThread2 extends Thread
{
Array a;
MyThread2(Array a){
this.a=a;
}
public void run(){
a.printNumbers(10);
}
}
class main{
public static void main(String args[]){
Array myArr = new Array();//only one object
MyThread1 t1=new MyThread1(myArr);
MyThread2 t2=new MyThread2(myArr);
t1.start();
t2.start();
}
}

Code with synchronization

Notice the difference in output​ between the code without synchronization (above) and the code with synchronization (below).

The t1 thread is fully completed before t2 starts.

class Array{
synchronized void printNumbers(int n){//method synchronized
for(int i=1;i<=3;i++){
System.out.println(n*i);
try{
Thread.sleep(500); //delayed a thread by 500 ms
}
catch(Exception e){System.out.println(e);}
}
}
}
class MyThread1 extends Thread
{
Array a;
MyThread1(Array a){
this.a=a;
}
public void run(){
a.printNumbers(1);
}
}
class MyThread2 extends Thread
{
Array a;
MyThread2(Array a){
this.a=a;
}
public void run(){
a.printNumbers(10);
}
}
class main{
public static void main(String args[]){
Array myArr = new Array();//only one object
MyThread1 t1=new MyThread1(myArr);
MyThread2 t2=new MyThread2(myArr);
t1.start();
t2.start();
}
}
Copyright ©2024 Educative, Inc. All rights reserved