In this article we will learn about what is synchronization? why synchronization is needed? and how to synchronize threads in Java along with sample programs.
Contents
Why Synchronization is Needed?
When two or more threads are accessing the same resource like a variable or a data structure, it may lead to inconsistent data or values. Such conditions that lead to inconsistency are known as race conditions.
As an example let’s consider a variable count whose value is 7 at present. Consider two operations: count = count + 1 which is executed by thread1 and another operation count = count – 1 which is executed by thread2. Note that both threads are sharing the common variable count.
If both threads execute in parallel then the sequence of operations can be either:
count = count + 1
count = count – 1
or
count = count – 1
count = count + 1
In both sequences the end value of count will be 7 which is a consistent value. But often a single high-level language statement will be converted to multiple assembly language statements.
The count = count + 1 will be converted to following assembly language statements:
A: R1 = count
B: R1 = R1 + 1
C: count = R1
Statements are labelled as A, B and C for convenience. R1 is a CPU register. Similarly count = count – 1 will be converted to following assembly language statements:
D: R2 = count
E: R2 = R2 – 1
F: count = R2
Again statements are labelled as D, E and F for convenience. R2 is another CPU register. Statements A, B and C are executed by thread1 in parallel with statements D, E and F of thread2.
Let the value of count be 7. Now consider the following statement execution sequence: A, B, D, E, C, F as shown below:
A: R1 = count (R1 = 7)
B: R1 = R1 + 1 (R1 = 8)
D: R2 = count (R2 = 7)
E: R2 = R2 – 1 (R2 = 6)
C: count = R1 (count = 8)
F: count = R2 (count = 6)
End value of count after the above execution sequence is 6 which is an inconsistent value and the execution sequence can be considered as an example that led to race condition.
To prevent race conditions we can use a mechanism known as synchronization.
What is Synchronization?
Synchronization is a mechanism which restricts simultaneous access to a shared resource by multiple threads.
Synchronization is used at large in operating systems like Windows, Linux, MacOSX etc. No synchronization might lead to a deadlock which is a serious problem. In Java, synchronization is supported by the synchronized keyword.
Using synchronized keyword we can create:
- Synchronized methods
- Synchronized blocks
Synchronization in Java
The key to synchronization in Java is monitor. A monitor is an object which is used for obtaining a mutual exclusive lock. Once a thread acquires a lock, it is said to have entered the monitor. When one thread is inside the monitor, no other thread is allowed to acquire a lock until that thread exits the monitor.
Every object in Java has an implicit monitor associated with it. To enter an object’s monitor, a method which is modified using synchronized keyword should be called.
Synchronized Methods
A method that is modified with the synchronized keyword is called a synchronized method. Syntax of a synchronized method is as follows:
synchronized return-type method-name(parameters)
{ … }
First let’s consider a program which doesn’t contain the synchronized method:
class MyName
{
void printName()
{
try
{
System.out.print("[My name ");
Thread.sleep(200);
System.out.print("is ");
Thread.sleep(500);
System.out.println("Suryateja]");
}
catch(Exception e)
{
System.out.println("Thread Interrupted");
}
}
}
class MyThread implements Runnable
{
Thread t;
MyName myname;
MyThread(MyName myname)
{
this.myname = myname;
t = new Thread(this);
t.start();
}
@Override
public void run()
{
myname.printName();
}
}
public class Driver
{
public static void main(String[] args)
{
MyName myname = new MyName();
MyThread thread1 = new MyThread(myname);
MyThread thread2 = new MyThread(myname);
MyThread thread3 = new MyThread(myname);
try
{
thread1.t.join();
thread2.t.join();
thread3.t.join();
}
catch(InterruptedException e)
{
System.out.println("Main thread is interrupted!");
}
System.out.println("Main thread terminated");
}
}
Output of the above program is:
[My name [My name [My name is is is Suryateja]
Suryateja]
Suryateja]
Main thread terminated
As you can observe in the above program, all three threads are executing the same method printName() simultaneously and hence the inconsistent output. For consistent output we can make the printName() method synchronized.
Below example demonstrates the use of synchronized methods in Java:
class MyName
{
synchronized void printName()
{
try
{
System.out.print("[My name ");
Thread.sleep(200);
System.out.print("is ");
Thread.sleep(500);
System.out.println("Suryateja]");
}
catch(Exception e)
{
System.out.println("Thread Interrupted");
}
}
}
class MyThread implements Runnable
{
Thread t;
MyName myname;
MyThread(MyName myname)
{
this.myname = myname;
t = new Thread(this);
t.start();
}
@Override
public void run()
{
myname.printName();
}
}
public class Driver
{
public static void main(String[] args)
{
MyName myname = new MyName();
MyThread thread1 = new MyThread(myname);
MyThread thread2 = new MyThread(myname);
MyThread thread3 = new MyThread(myname);
try
{
thread1.t.join();
thread2.t.join();
thread3.t.join();
}
catch(InterruptedException e)
{
System.out.println("Main thread is interrupted!");
}
System.out.println("Main thread terminated");
}
}
Output of the above program is:
[My name is Suryateja]
[My name is Suryateja]
[My name is Suryateja]
Main thread terminated
In the above example synchronized method is printName() of MyName class.
Synchronized Blocks
Sometimes while working with third-party classes, we might want to synchronize the access to methods in those classes. In such cases, synchronized methods cannot be used. Instead we can use synchronized blocks. Syntax of a synchronized block is as follows:
synchronized(object-reference)
{ //Statements to be synchronized }
The above program can be modified to contain a synchronized block as shown below:
class MyName
{
void printName()
{
try
{
System.out.print("[My name ");
Thread.sleep(200);
System.out.print("is ");
Thread.sleep(500);
System.out.println("Suryateja]");
}
catch(Exception e)
{
System.out.println("Thread Interrupted");
}
}
}
class MyThread implements Runnable
{
Thread t;
MyName myname;
MyThread(MyName myname)
{
this.myname = myname;
t = new Thread(this);
t.start();
}
@Override
public void run()
{
synchronized(myname)
{
myname.printName();
}
}
}
public class Driver
{
public static void main(String[] args)
{
MyName myname = new MyName();
MyThread thread1 = new MyThread(myname);
MyThread thread2 = new MyThread(myname);
MyThread thread3 = new MyThread(myname);
try
{
thread1.t.join();
thread2.t.join();
thread3.t.join();
}
catch(InterruptedException e)
{
System.out.println("Main thread is interrupted!");
}
System.out.println("Main thread terminated");
}
}
Output of the above program is:
[My name is Suryateja]
[My name is Suryateja]
[My name is Suryateja]
Main thread terminated
In the above program the call to the method printName() is written inside a synchronized block.
Suryateja Pericherla, at present is a Research Scholar (full-time Ph.D.) in the Dept. of Computer Science & Systems Engineering at Andhra University, Visakhapatnam. Previously worked as an Associate Professor in the Dept. of CSE at Vishnu Institute of Technology, India.
He has 11+ years of teaching experience and is an individual researcher whose research interests are Cloud Computing, Internet of Things, Computer Security, Network Security and Blockchain.
He is a member of professional societies like IEEE, ACM, CSI and ISCA. He published several research papers which are indexed by SCIE, WoS, Scopus, Springer and others.
Leave a Reply