Multithreading in Java Real time example

By | October 17, 2023

What is Multithreading in Java?

In multithreading, a program is divided into two or more subprograms or threads that can run concurrently (at the same time) in parallel or concurrently in a shared memory space. Each thread represents a separate flow of control within the program, allowing multiple operations to be performed simultaneously.

Multithreading is used to improve the performance and response of the application by utilizing the multicore processors of modern computer systems. In Java, Multithreading can be achieved through the Thread class or Runnable interface.

Real time example of Multithreading in Java – Bank Account example

A bank account is the classic example of multithreading, where a husband and wife try to withdraw from the same account concurrently (at the same time). It causes the race condition (when two or more threads concurrently attempt to modify shared data, which leads to unpredictable and incorrect behaviour of the program).

We implement the above example and see how the race condition is causing the incorrect behaviour in the bank account. After that, we introduce the multithreading concepts to resolve it.

Steps to implement the Multithreading in Java real time example

Step 1- Create model class

First, we create a model class, Account. It has a filed balance to represent the account balance (hard code the balance to $10000), a getter method for the balance filed, and a custom withdraw(double amount) method that updates the balance as someone withdraws any amount.

Java
/**
 * Multi-threadin Bank Account Example
 * 
 * 1. Currently, we hard-code the balance amount to $10000
 * 2. withdraw method will took the withdraw amount
 *    and update the balance
 * 3. Withdraw will happen even if there is not 
 *   enough balance in the account
 * 
 * @author paulsofts
 */
public class Account {
	
	private double balance = 10000;

	public double getBalance() {
		return balance;
	}

	public void withdraw(double amount) {
		balance = balance - amount;
	}

}

Step 2- Create AccountHolder class

Now, we create a class that will manage all the transactions in the bank account. For this, we create the AccountHolder class and implements the Runnable interface, and override the run() method to perform multiple transactions. In each transaction, it withdraws $3000 and prints the thread name and the withdrawal amount as a message.

Java
/**
 * 1. AccountHolder class implements Runnable interface
 * 2. Override the run() method of Runnable interface such that,
 * 	  each time it will withdraw $3000.
 * 3. The withdrawAmount() method will check if the balance is 
 *    greater than withdraw amount then it will print the thread 
 *    name and the withdraw amount 
 * 4. Adding synchronized keyword over the withdrawAmount() method
 * 
 * @author paulsofts
 */

public class AccountHolder implements Runnable {

	private Account account;
	
	public AccountHolder(Account account) {
		this.account = account;
	}

	@Override
	public void run() {
		for(int i=0; i<3; i++) {
			withdrawAmount(3000);
			if(account.getBalance() < 0) {
				System.out.println("Something went wrong!!! Please, check your balance");
			}
		}
	}
	
	public void withdrawAmount(double withdrawAmount) {
		if(account.getBalance() >= withdrawAmount) {
			System.out.println(Thread.currentThread().getName() + " is making a withdraw of $ " + withdrawAmount);
			try {
				Thread.sleep(3000);
			}catch(InterruptedException e) {
				
			}
			account.withdraw(withdrawAmount);
		}else {
			System.out.println("Not enough balance " + Thread.currentThread().getName() + " tries to withdraw. Remaining Balance " + account.getBalance());
		}
		
	}
}

Step 3- Create Main class

Now, we create a main class to test our application. We create two threads and set the names of the threads to see which thread is invoking the transaction.

Java
public class Main {
	
	public static void main(String[] args) {
		
		Account account = new Account();
		AccountHolder accountHolder = new AccountHolder(account);
		Thread t1 = new Thread(accountHolder);
		Thread t2 = new Thread(accountHolder);
		t1.setName("Rahul");
		t2.setName("Anjali");
		t1.start();
		t2.start();
	}

}

Once we run our application, we get the following output: As you can see, we are getting unpredictable behavior as we have a total balance of $10000, and here, people have withdrawn $12000, and the balance goes to a negative value also.

Multithreading Real time example in Java
Fig 1- Output: Race Condition

In order to resolve this issue, we need to add a lock over the withdrawAmount(double withdrawAmount) method. For this, we use the synchronized keyword, which will let one-and-only one thread to execute that method until it finishes; the other threads have to wait.

Java
/**
 * 1. AccountHolder class implements Runnable interface
 * 2. Override the run() method of Runnable interface such that,
 * 	  each time it will withdraw $3000.
 * 3. The withdrawAmount() method will check if the balance is 
 *    greater than withdraw amount then it will print the thread 
 *    name and the withdraw amount 
 * 4. Adding synchronized keyword over the withdrawAmount() method
 * 
 * @author paulsofts
 */

public class AccountHolder implements Runnable {

	private Account account;
	
	public AccountHolder(Account account) {
		this.account = account;
	}

	@Override
	public void run() {
		for(int i=0; i<3; i++) {
			withdrawAmount(3000);
			if(account.getBalance() < 0) {
				System.out.println("Something went wrong!!! Please, check your balance");
			}
		}
	}
	
	public synchronized void withdrawAmount(double withdrawAmount) {
		if(account.getBalance() >= withdrawAmount) {
			System.out.println(Thread.currentThread().getName() + " is making a withdraw of $ " + withdrawAmount);
			try {
				Thread.sleep(3000);
			}catch(InterruptedException e) {
				
			}
			account.withdraw(withdrawAmount);
		}else {
			System.out.println("Not enough balance " + Thread.currentThread().getName() + " tries to withdraw. Remaining Balance " + account.getBalance());
		}
		
	}
}

Below, we can see that our application is now working properly, even if multiple threads are trying to make transactions concurrently.

Multithreding Banking example
Fig 2- Multithreding Banking example output

Leave a Reply

Your email address will not be published. Required fields are marked *