Thread Synchronization in Java

Category: Java   Tags: Java, Java Thread, Java MultiThread, Thread Synchronization

Sometimes multiple threads try to access the same resources at the same time, therefore there should be a mechanism that ensure that the shared resources are used by a single thread at a time. The process by which this is achieved is called synchronization.

Intrinsic Locks and Synchronization

Synchronization is built around the intrinsic lock or monitor lock (Often referred simply as a "monitor"). Every object has an intrinsic lock associated with it. When a thread needs exclusive access to an object has to first acquire that object's intrinsic lock and release it once it is done with them.

Once a thread acquire the intrinsic lock, no other thread can acquire the same lock and will have to wait till it is released by the current thread. Each object has just one lock.

You can synchronize the code in two ways: using synchronized method or Synchronized block.

Using Synchronized Methods

A synchronized method is the way to enter an object's monitor. While a thread is inside a synchronized method, all other threads that try to call it on the same instance have to wait. A synchronized method has below syntax:

                          accessModifier returnType synchronized methodName([parameter1, .., parameterN]) {}
                        

To understand synchronized method, first understand that why we need synchronization. Here is a simple Java example:

JavaSyncMethodExample.java

                            package com.tutorial.javabasic;

                            class Counter {

                                public void printOrder() {
                                    for (int i = 1; i < 5; i++) {
                                        System.out.println(i);
                                        try {
                                            Thread.sleep(500);
                                        } catch (InterruptedException ex) {
                                            ex.printStackTrace();
                                        }
                                    }
                                    System.out.println("-----------------");
                                }
                            }

                            class MyThread implements Runnable {

                                Counter counter;

                                MyThread(Counter c) {
                                    counter = c;
                                    new Thread(this).start();
                                }

                                @Override
                                public void run() {
                                    counter.printOrder();
                                }
                            }

                            public class JavaSyncMethodExample {

                                public static void main(String s[]) {
                                    Counter sharedInstance = new Counter();
                                    new MyThread(sharedInstance);
                                    new MyThread(sharedInstance);
                                    new MyThread(sharedInstance);
                                }
                            }
                        

Output:

                            1
                            1
                            1
                            2
                            2
                            2
                            3
                            3
                            3
                            4
                            4
                            4
                            -----------------
                            -----------------
                            -----------------
                          

Here we created three thread and all thread is accessing printOrder() method of Counter class on the same instance sharedInstance.

Inside printOrder method we are using Thread.sleep(500) to pause the current thread, during this period other thread enter the printOrder method and start its own loop therefore the order of printed number is not consistent as shown by the output.

All these inconsistency happed because printOrder method was not synchronized. So to make it consistent we have to synchronize the printOrder method using synchronized keyword:

                            public synchronized void printOrder() {
                                for (int i = 1; i < 5; i++) {
                                    System.out.println(i);
                                    try {
                                        Thread.sleep(500);
                                    } catch (InterruptedException ex) {
                                        ex.printStackTrace();
                                    }
                                }
                                System.out.println("-----------------");
                            }
                          

Now as printOrder method is synchronized, only one thread can enter printOrder method and other will wait till the current thread doesn't exit. This change will make the output consistent:

                            1
                            2
                            3
                            4
                            -----------------
                            1
                            2
                            3
                            4
                            -----------------
                            1
                            2
                            3
                            4
                            -----------------
                          

Using Synchronized Block

Imagine you are working with a third party library class and you have no access to source code, how will you synchronize a method? This is where synchronized block comes in the picture. It has below syntax:

                            synchronized(objReference) {
                                // Your code
                            }
                          

objReference is a reference to the object being synchronized. We will apply this to our previous example:

JavaSyncBlockExample.java

                            package com.tutorial.javabasic;

                            class Counter {

                                public void printOrder() {
                                    for (int i = 1; i < 5; i++) {
                                        System.out.println(i);
                                        try {
                                            Thread.sleep(500);
                                        } catch (InterruptedException ex) {
                                            ex.printStackTrace();
                                        }
                                    }
                                    System.out.println("-----------------");
                                }
                            }

                            class MyThread implements Runnable {

                                Counter counter;

                                MyThread(Counter c) {
                                    counter = c;
                                    new Thread(this).start();
                                }

                                @Override
                                public void run() {
                                    synchronized(counter) {
                                        counter.printOrder();
                                    }
                                }
                            }

                            public class JavaSyncBlockExample {

                                public static void main(String s[]) {
                                    Counter c = new Counter();
                                    new MyThread(c);
                                    new MyThread(c);
                                    new MyThread(c);
                                }
                            }
                        

As we are sharing the Counter class method among all thread, we are passing the instance of Counter in synchronized block and calling printOrder method from inside:

                            synchronized(counter) {
                                counter.printOrder();
                            }