š¦ Bank Transaction Concurrency Problem

Problem Statement
Multiple threads (ATM users, online banking) access shared bank accounts.
Operations:
deposit(amount)withdraw(amount)getBalance()
Requirements:
Thread-safe: No race conditions during concurrent transactions.
Consistency: Balance never goes negative (unless overdraft allowed).
Deadlock-free: No circular waits between accounts (e.g., transfer between A and B).
š” Solution Approaches
1. Basic Thread-Safe Account (Synchronized Methods)
To make the BankAccount thread-safe, we start by marking the deposit, withdraw, and getBalance methods as synchronized so only one thread can access them at a time per object.
Key Mechanics
Every Java object has an intrinsic lock (monitor).
synchronizedmethods acquire this lock before execution.Only one thread can hold the lock at a time ā all other threads block.
deposit() and withdraw() at the same time on the same object, so balance remains consistent.- š”ā However, if two threads operate on different objects, synchronization won't help because each object has a different lock.
public class BankAccount {
private double balance;
public synchronized void deposit(double amount) {
balance += amount;
}
public synchronized void withdraw(double amount) throws InsufficientFundsException {
if (balance < amount) throw new InsufficientFundsException();
balance -= amount;
}
public synchronized double getBalance() {
return balance;
}
}

Case 1: Simple Deposit-Withdraw (No Conflict)
BankAccount account = new BankAccount();
// Thread 1
account.deposit(100); // ā
Balance = 100
// Thread 2
account.withdraw(30); // ā
Balance = 70
Case 2: Concurrent Deposits (Lost Update Problem Prevented)
Case 3: Overdraft Prevention
// Thread 1
account.withdraw(150); // ā Fails (balance=100)
// Thread 2
account.getBalance(); // ā
Returns 100 (consistent read)
Where It Fails (Edge Cases)
Problem 1: Nested Operations Deadlock
Consider if we are going to use this Object and tranfering money from one account to another
// Thread 1
synchronized void transfer(BankAccount to, double amount) {
this.withdraw(amount); // Already holds lock on 'this'
to.deposit(amount); // Needs lock on 'to' ā DEADLOCK if another thread does reverse transfer
}
Problem 2: Liveness Issues (Performance)
All operations serialize (even
getBalance()blocks writes).Under high contention, throughput plummets.
Problem 3: Compound Actions Not Atomic
if (account.getBalance() >= 100) { // Race condition!
account.withdraw(100); // Balance may change between check and withdraw
}
// Operation is not Atomic, may cause issue if any changes happen between two operations.
