Thread synchronization in Java


Java Synchronization ThreadViews 3831

Thread synchronization in Java

Thread synchronization is an important concept in Java Multithreading. It is important to achieve thread-safety since in multithreading uses shared resources. We can implement thread-safety using synchronized block and method in Java. Synchronization ensures the control of multiple threads accessing the same resource by allowing only a single thread to access the shared resource.

The main use of Thread Synchronization in Java is to avoid thread interference and prevent inconsistency. When we allow multiple threads to work on the same data, there are chances of data corruption and it might lead to concurrency issues. For example, if two threads access the same file, where 1 thread is writing the file and the other closes the file. This situation can corrupt the file. Hence it is important to synchronize threads. We can achieve this using multiple ways using the synchronized keyword which we will see in detail in this tutorial.

How Thread synchronization works in Java

We can implement synchronization in Java by using the concept of monitors. Whenever a thread tries to access a shared resource, it acquires a monitor. At a time only one thread can own a monitor. During synchronization, when a thread acquires a lock, it enters the monitor state. Hence it prevents other threads from accessing the lock on the shared resource and entering the monitor. This avoids concurrency and corruption issues. Once the thread completes the action using the locked resource, it releases the lock for other threads to use.

Types of Synchronization

There are 2 types of synchronization in Java:

  • Process synchronization
  • Thread synchronization

Thread Synchronization in Java

Multithreading without synchronization

Before we understand synchronization, let’s understand the issue related to multithreading without synchronization with the below example.

When we do not use synchronization we get an inconsistent output which means it is not in proper order.

class PrintDemo {
  void display(int x) {
    for(int i=1;i<=3;i++) {
      System.out.println(x+i);
      try {
        Thread.sleep(200);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
      
  }
}

class ThreadA extends Thread {
  PrintDemo p;
  
  ThreadA(PrintDemo p){
    this.p = p;
  }
  public void run() {
    p.display(2);
  }
  
}

class ThreadB extends Thread {
  PrintDemo p;
  
  ThreadB(PrintDemo p){
    this.p = p;
  }
  public void run() {
    p.display(4);
  }
}

public class SyncDemo {

  public static void main(String[] args) {
    PrintDemo p = new PrintDemo();
    ThreadA ta = new ThreadA(p);
    ThreadB tb = new ThreadB(p);
    
    ta.start();
    tb.start();

  }

}
3
5
4
6
5
7

Multithreading with Thread synchronization in Java

Now, let us see the same example by using the synchronized keyword for the display method. We can observe that the output is consistent and is in proper order. Since we have used the synchronized method, it allows only 1 thread to execute at a time. Hence first ThreadA executes completely after which ThreadB executes.

class PrintDemo {
  synchronized void display(int x) {
    for(int i=1;i<=3;i++) {
      System.out.println(x+i);
      try {
        Thread.sleep(200);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
      
  }
}

class ThreadA extends Thread {
  PrintDemo p;
  
  ThreadA(PrintDemo p){
    this.p = p;
  }
  public void run() {
    p.display(2);
  }
  
}

class ThreadB extends Thread {
  PrintDemo p;
  
  ThreadB(PrintDemo p){
    this.p = p;
  }
  public void run() {
    p.display(4);
  }
}

public class SyncDemo {

  public static void main(String[] args) {
    PrintDemo p = new PrintDemo();
    ThreadA ta = new ThreadA(p);
    ThreadB tb = new ThreadB(p);
    
    ta.start();
    tb.start();

  }

}
3
4
5
5
6
7

Synchronized block

We can synchronize a specific piece of code inside a method by using the synchronized keyword. In this way, we do not need to synchronize the entire method. Below is an example of a synchronized block that implements Thread synchronization in Java. Only the block of code inside the synchronized block is synchronized. In this case, it allows only 1 thread to access this code at a time.

class displayDemo {
  public void display(String text) {
    synchronized(this) {
      System.out.println("Welcome " + text);
      try {
        Thread.sleep(200);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println("Exiting " + text);
    }
  }
}

class displayThread extends Thread {
  private String text;
  displayDemo d;
  
  displayThread(String text, displayDemo d) {
    this.text = text;
    this.d = d;
  }
  
  public void run() {
    d.display(text);
  }
}

public class SyncBlockDemo {

  public static void main(String[] args) {
    displayDemo d = new displayDemo();
    
    displayThread dt1 = new displayThread("Java", d);
    displayThread dt2 = new displayThread("Thread", d);
    
    dt1.start();
    dt2.start();

  }

}
Welcome Java
Exiting Java
Welcome Thread
Exiting Thread

In some situations, a synchronized block might not be useful. Consider a situation where 2 threads want to only read the content inside the block and update. In this case, we can use Semaphore or read/write locks which we will discuss in upcoming tutorials.

Synchronized method

Another way to achieve thread synchronization in Java is to synchronize the whole method so that at a time only a single thread accesses the method. For this, we use the synchronized keyword before the method. This will avoid concurrency and inconsistency problems. Below is the same example using the synchronized method. In both cases, we will get the same output.

class displayDemo {
  public synchronized void display(String text) {
    System.out.println("Welcome " + text);
    try {
      Thread.sleep(200);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Exiting " + text);
    
  }
}

class displayThread extends Thread {
  private String text;
  displayDemo d;
  
  displayThread(String text, displayDemo d) {
    this.text = text;
    this.d = d;
  }
  
  public void run() {
    d.display(text);
  }
}

public class SyncBlockDemo {

  public static void main(String[] args) {
    displayDemo d = new displayDemo();
    
    displayThread dt1 = new displayThread("Java", d);
    displayThread dt2 = new displayThread("Thread", d);
    
    dt1.start();
    dt2.start();

  }

}
Welcome Java
Exiting Java
Welcome Thread
Exiting Thread

Static synchronization

We can use the synchronized keyword for both static and non-static methods. It works the same way as a synchronized non-static method.

Static synchronization synchronizes the class and not the object. This is another way of implementing Thread synchronization in Java.

class displayDemo {
  public static synchronized void display(String text) {
    System.out.println("Welcome " + text);
    try {
      Thread.sleep(200);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Exiting " + text);
    
  }
}

Inter-thread communication

Inter-thread communication or cooperation is the other way to implement Thread synchronization in Java, It controls the communication between the synchronized threads. This means it allows another priority thread to enter the critical section by pausing the currently executing thread. For this, inter-thread communication is very important else it can result in critical issues. We can implement this using the wait(), notify(), and notifyAll() methods of the Object class. We will discuss this in detail in a separate tutorial.

 

Reference

Translate ยป