Curriculum
Course: Learn Java Programming
Login

Curriculum

Learn Java Programming

Text lesson

Understanding The Thread Synchronization Concept in Java

[post-views]

 

 

In this lesson, you will learn.

  • What is the Problem
  • What is Synchronization in Java?
  • Examples

 

What is The Problem?

1. Banking Analogy: Suppose a bank teller desk where only one customer can be served at a time. If multiple customers try to interact with the teller simultaneously, they may cause confusion or errors (like incorrect withdrawals or deposits).

Now, we can consider

Teller Desk: Shared Resource

Customer: Thread

 

2. Bridge Analogy: Consider a narrow bridge that can only accommodate one vehicle at a time. If vehicles from both ends try to cross simultaneously, accidents or gridlock could occur.

Now, we can consider

Bridge: Shared Resource

Vehicle: Thread

 

In both analogies, we need to design a mechanism to ensure that only one customer can be served at the bank teller desk and only one person crosses the bridge at any given time to maintain safety and data consistency.

 

What is Synchronization in Java?

Synchronization is a process that ensures that a shared resource is only used by one thread at a time when multiple threads need access to it.

Without synchronization, multiple threads can interfere with each other, leading to inconsistent or erroneous behavior.

Synchronization is essential when threads interact with shared data or objects.

 

Working Process of Synchronization

Here is the process of how multiple thread works in the synchronization.

 

 

Example: Imagine an office where multiple employees need to print their documents using a single shared printer. If there is no orderly process or queue to access the printer, several people may attempt to print their documents at the same time. As a result:

  • Documents could get mixed up, leading to confusion.
  • Print jobs may overlap, causing pages from different documents to be interleaved.
  • Some documents might not print correctly or might be lost entirely.

 

Implementing Thread Synchronization in Java

You can synchronize your code in either of two ways. Both involve the use of the synchronized keyword.

 

 

 

1. Synchronized Methods

When a thread is inside a synchronized method, all other threads that try to call it (or any other synchronized method) on the same instance have to wait.

 

Syntax

public synchronized void synchronizedMethod()
{
        // Method code that requires synchronization
}

 

 

 

2. Synchronized Blocks

A synchronized block ensures that a particular section of code can only be executed by one thread at a time.

This ensures that only one thread can execute a synchronized block at any given time.

 

Syntax

The syntax of a synchronized block is as follows:

synchronized(objRef) {
 // statements to be synchronized
}

 

Here, objRef is a reference to the object being synchronized.

 

Example 1: Without Synchronization

The following program may or may not print a counter value in the sequence and every time it produces a different result based on the availability of the CPU for a thread.

package synchronization;

class Counter {
	static int count = 0;
	
	public static void setCount() {
		for (int i = 1; i <=1000; i++) {
			Counter.count++;
		}
	}
}
class MyThread extends Thread {
	
	private int threadNo;
			
	public MyThread(int threadNo) {
		this.threadNo = threadNo;
	}
	
	@Override
	public void run() {
		Counter.setCount();
		System.out.println("The thread "+threadNo+" is over.");
	}
}

public class CounterMain {

	public static void main(String[] args) throws InterruptedException {
		MyThread t1 = new MyThread(1);
		MyThread t2 = new MyThread(2);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println("The value of the counter is: "+Counter.count);
	}
}

 

Output – First Run

The thread 1 is over.
The thread 2 is over.
The value of the counter is: 1290

 

Output – Second Run

The thread 2 is over.
The thread 1 is over.
The value of the counter is: 1070

 

Explanation

In the above example, each thread increments the counter 1000 times. Ideally, the counter should reach 2,000. However, due to concurrent modifications without synchronization, you will likely see a different, incorrect result every time you run this program.

 

Example -2: With Synchronization

Now, let’s add synchronization to ensure that the counter increments correctly without interference between threads:

&nbsp;package synchronization;

class Counter {
	static int count = 0;
	
	// Increment the counter without synchronization
	public synchronized static void setCount() {
		for (int i = 1; i <=1000; i++) {
			Counter.count++;
		}
	}
}
class MyThread extends Thread {
	
	private int threadNo;
			
	public MyThread(int threadNo) {
		this.threadNo = threadNo;
	}
	
	@Override
	public void run() {
		Counter.setCount();
		System.out.println("The thread "+threadNo+" is over.");
	}
}

public class CounterMain {

	public static void main(String[] args) throws InterruptedException {
		MyThread t1 = new MyThread(1);
		MyThread t2 = new MyThread(2);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println("The value of the counter is: "+Counter.count);
	}
}

 

Output

The thread 1 is over.
The thread 2 is over.
The value of the counter is: 2000

 

Explanation

  • Without Synchronization: The counter result is unpredictable because multiple threads access and modify the shared count variable simultaneously.
  • This leads to a race condition where several threads read and write to the variable at the same time, resulting in lost updates.
  • With Synchronization: By making the setCount() method synchronized, we lock the counter instance for each increment operation, ensuring that only one thread can execute the method at any given time.
  • This prevents race conditions and ensures that each increment operation completes before the next one starts, leading to the correct final count of 2,000.

 

Example: Synchronized Block: Printing a Table

package synchronization;

class NumberPrinter  
{      
	void printTable(int n) {    
		synchronized(this)//synchronized block
		{    
			for(int i=1;i<=5;i++){    
				System.out.println(n*i);    
				try{    
					Thread.sleep(400);    
				}catch(Exception e){System.out.println(e);}    
			}
		}
		for (int i = 1; i < 5; i++) {
			try {
				Thread.sleep(2500);
				System.out.println(Thread.currentThread()+" is executing");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	
		}
	}//end of the method    
}    

class MyThread1 extends Thread{    
	NumberPrinter t;    
	MyThread1(NumberPrinter t){    
		this.t=t;    
	}    
	public void run(){    
		t.printTable(3);    
	}    

}    
class MyThread2 extends Thread{    
	NumberPrinter t;    
	MyThread2(NumberPrinter t){    
		this.t=t;    
	}    
	public void run(){    
		t.printTable(10);    
	}    
}    

public class SynchronizedTable{    
	public static void main(String args[]){    
		NumberPrinter obj = new NumberPrinter();//only one object    
		MyThread1 t1=new MyThread1(obj);    
		MyThread2 t2=new MyThread2(obj);    
		t1.start();    
		t2.start();    
	}    
}    

 

Output: First Run

10
20
30
40
50
3
6
9
12
15
Thread[Thread-1,5,main] is executing
Thread[Thread-0,5,main] is executing
Thread[Thread-1,5,main] is executing
Thread[Thread-0,5,main] is executing
Thread[Thread-1,5,main] is executing
Thread[Thread-0,5,main] is executing
Thread[Thread-1,5,main] is executing
Thread[Thread-0,5,main] is executing

 

Output: Second Run

3
6
9
12
15
10
20
30
40
50
Thread[Thread-0,5,main] is executing
Thread[Thread-1,5,main] is executing
Thread[Thread-0,5,main] is executing
Thread[Thread-1,5,main] is executing
Thread[Thread-0,5,main] is executing
Thread[Thread-1,5,main] is executing
Thread[Thread-0,5,main] is executing
Thread[Thread-1,5,main] is executing

 

 

 


 

End of the lesson….enjoy learning

 

 

Student Ratings and Reviews

 

 

 

There are no reviews yet. Be the first one to write one.

 

 

Submit a Review

 

 

Layer 1
Login Categories