Java is a multi-threaded language. So, it supports threads that allow multiple blocks of code to execute simultaneously. Multi-threading can greatly increase the performance of an application. However, developers need to take care to ensure that shared resources are accessed properly. Otherwise, multi-threading can cause unwanted results. The process by which Java helps to control access to shared resources by multiple threads is called synchronization. In this article, we will be learning more about synchronization and how it works.

  1. Thread Basics
  2. Need for synchronization
  3. Synchronized Method
  4. Synchronized Block
  5. Synchronized Method v/s Synchronized Block

Synchronization in Java

Thread Basics

Before we start learning about synchronization, let us take a quick look at how threads can be created and how they work. Java has an in-built class called java.lang.Thread. In order to create a Thread, you need to create a class that extends this Thread class. The class also needs to have a method called run where you need to specify the code to be executed independently. Finally, you need to kick off the thread by invoking the start method.

Sample Code

public class MyThread extends Thread{
 public void run() {
  System.out.println("Starting thread");
  for(int i=0;i<3;i++) {
    System.out.println("Hello:"+i);
  }
  System.out.println("Ending thread");
}

public static void main(String[] args) {
   System.out.println("Starting main");
   MyThread t = new MyThread();
   t.start();
   System.out.println("Ending main");
 }
}
  • This code specifies a class called MyThread which extends Thread
  • Line 3 specifies a run method. This method has a for loop that simply prints the text “Hello” 3 times.
  • Line 10 specifies a main method. It first creates a MyThread object t. It then invokes the start method on t. This kicks off the thread and executes the code specified in the run method. Meanwhile, the code in the main method executes simultaneously.

Output

Starting main
Ending main
Starting thread
Hello:0
Hello:1
Hello:2
Ending thread

Since there is no guarantee about the sequence in which threads run, the output may be slightly different each time the code is executed.
There is another way to create a Thread. Java provides an in-built interface called java.lang.Runnable. So, you can also create a Thread by creating a class that implements the Runnable interface and provide an implementation for the run method.

Need for synchronization

As explained earlier, synchronization helps to control access to a shared resource. So, when multiple threads try to access a resource, it ensures that only one thread can access it at a time. Without synchronization, the code can result in unwanted results.

Sample Code

public class Greeting {
 public void showGreeting() {
  for(int i=0;i < 3;i++) {
   System.out.println("Hello "+i);
  }
 }
}
  • This code specifies a class called Greeting
  • Line 2 defines a method called showGreeting which has a for loop that prints the text “Hello” 3 times.

Now, consider the following code:

public class GreetingThread extends Thread{

 private Greeting greeting;

 GreetingThread(Greeting greeting){
  this.greeting = greeting;
 }

 public void run() {
  greeting.showGreeting();
 }

 public static void main(String[] args) {
  Greeting greeting = new Greeting();
  Thread thread1 = new GreetingThread(greeting);
  Thread thread2 = new GreetingThread(greeting);
  thread1.start();
  thread2.start();
 }
}
  • This code specifies a class called GreetingThread which extends Thread
  • Line 3 specifies a private field called greeting
  • Line 5 specifies a constructor which initializes the greeting field
  • Line 9 specifies a run method. It invokes the showGreeting method on the greeting field
  • Line 13 specifies a main method. It first creates a Greeting object called greeting.
  • Lines 15-16 create two GreetingThread objects called thread1 and thread2. Note that both use the greeting object created at Line 14
  • Finally, Lines 17-18 kick off the threads by invoking the start method on thread1 and thread2

Output

Hello 0
Hello 0
Hello 1
Hello 2
Hello 1
Hello 2

Both threads execute the showGreeting method on the same greeting object. Thus, while thread1 is executing the greeting.showGreeting, thread2 also executes it. This results in the outputs of both threads getting mixed up.

Synchronized Method

A synchronized method is simply a method that has the synchronized keyword specified in the method declaration. When a method is declared as synchronized, only one thread can access it at a time on a particular object. So, if a thread is executing a synchronized method and if another thread tries to execute it on the same object, the second thread needs to wait until the first thread completes its execution.

Sample Code

public class Greeting {

 public synchronized void showGreeting() {
  for(int i=0;i < 3;i++) {
    System.out.println("Hello "+i);
  }
 }
}
  • The showGreeting method in the Greeting class is now modified. It is declared as synchronized.
  • Thus, only one thread can now invoke the showGreeting method at a time on a Greeting object.

Output
Hello 0
Hello 1
Hello 2
Hello 0
Hello 1
Hello 2

Since the showGreeting method is now synchronized, only one thread can execute it at a time. Thus, when either thread1 or thread2 starts executing showGreeting, it prints Hello 3 times. The other thread has to wait. Only after the first thread completes, the second thread executes and prints Hello 3 times.

Synchronized Block

In addition to methods, a block of code can also be synchronized. Sometimes, you may have a large method out of which only a small part of the code may need to be synchronized. In such cases, if you declare the whole method as synchronized, only one thread can execute the method and other threads will be made to wait unnecessarily. Synchronized blocks are useful in such situations, they allow you to synchronize only a small part of the method. In order to create a synchronized block, you need to specify the synchronized keyword followed by an object in parentheses. Thus, when multiple threads try to execute the code in the synchronized block on an object, only one thread can execute it at a time.

Sample Code

public class Greeting {
 public void showGreeting() {
  synchronized (this) {
   for (int i = 0; i < 3; i++) {
    System.out.println("Hello " + i);
   }
 }
}
}

  • The showGreeting method in the Greeting class is no longer synchronized
  • Line 3 specifies a synchronized block. It consists of the keyword synchronized followed by the this keyword in brackets. This indicates that the code that follows needs to be synchronized on the object on which it is invoked. Thus when multiple threads try to invoke the showGreeting method on the same object, only one thread can execute it at a time.

Synchronized Method v/s Synchronized Block

There are several differences between a synchronized method and a synchronized block. These are as follows:

  • When a method is synchronized, it is synchronized on the object on which the method is invoked. A synchronized block on the other hand is synchronized on the object specified in parentheses after the synchronized keyword.
  • When a thread is executing a synchronized method, no other thread can execute any other synchronized method on the same object.
  • When a code block is synchronized, other threads can execute the rest of the code in the method.
    Synchronized blocks are generally preferable over synchronized blocks particularly for large methods since they allow other threads to execute the rest of the code.

Conclusion

So, in this article, we took a look at how threads work. We understood the need for synchronization and how synchronized methods and synchronized blocks work. Finally, we learned the difference between synchronized methods and blocks.