It is easy to understand and expect the output when there is only a single thread running in Java application. You are able to process code sequentially and expect the output. Things starts getting tricky when two or more threads are working at same time.
Check out the following example, can you figure out what can go wrong in process of spending money. 2 different threads are checking the balance in the same account and trying to withdraw amount at same time. It could happen that both thread check if balance is greater than the amount to be spent and amount will get overdrawn.
package com.shrikane.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ConcurrencyInJava {
public static void main(String[] args) {
final BankAccount bankAccount = new BankAccount(100);
final WithdrawMoney withdrawMoney1 = new WithdrawMoney(bankAccount, 10);
final WithdrawMoney withdrawMoney2 = new WithdrawMoney(bankAccount, 100);
final ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(withdrawMoney1);
executorService.execute(withdrawMoney2);
executorService.shutdown();
}
static class WithdrawMoney implements Runnable {
private final BankAccount bankAccount;
private final int amountToWithdraw;
WithdrawMoney(
final BankAccount bankAccount,
final int amountToWithdraw) {
this.bankAccount = bankAccount;
this.amountToWithdraw = amountToWithdraw;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
if (bankAccount.balance > amountToWithdraw) {
System.out.println(String.format("Withdraw Successful %s", threadName));
bankAccount.spendAmount(amountToWithdraw);
} else {
System.out.println(String.format("Withdraw Failed %s", threadName));
}
}
}
static class BankAccount {
private int balance;
BankAccount(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void spendAmount(int amountToSpend) {
balance = balance - amountToSpend;
if (balance < 0) {
System.out.println(String.format("Amount Overdrawn %s", Thread.currentThread().getName()));
}
}
}
}
So, the problematic output looks like Withdraw Successful pool-1-thread-1 Withdraw Successful pool-1-thread-2 Overdrawn
So, let’s see how this problem can be solved. we need to acquire lock before this 2-part transaction takes place.
- Checking if account has sufficient balance
- And then withdrawing the money.
And we use the word “synchronized” to solve the problem.
@Override
public void run() {
synchronized (bankAccount) {
String threadName = Thread.currentThread().getName();
if (bankAccount.balance > amountToWithdraw) {
System.out.println(String.format("Withdraw Successful %s", threadName));
bankAccount.spendAmount(amountToWithdraw);
} else {
System.out.println(String.format("Withdraw Failed %s", threadName));
}
}
}
New correct output looks like Withdraw Successful pool-1-thread-1 Withdraw Failed pool-1-thread-2
A WordPress Commenter
Hi, this is a comment.
To get started with moderating, editing, and deleting comments, please visit the Comments screen in the dashboard.
Commenter avatars come from Gravatar.