public class JointBankAccount {
static class BankAccount {
private String accountId;
private int balance;
public BankAccount(String accountId, int balance) {
this.accountId = accountId;
this.balance = balance;
}
// 1. synchronized protects this specific account instance
public synchronized void withdraw(int amount) {
String name = Thread.currentThread().getName();
// 2. Log the attempt with the Account ID
System.out.println("[" + accountId + "] " + name + " is requesting $" + amount + "...");
if (balance >= amount) {
System.out.println("[" + accountId + "] " + name + " is verifying balance...");
try {
Thread.sleep(100); // Simulate processing time
} catch (InterruptedException e) {
e.printStackTrace();
}
// 3. Safe modification
balance -= amount;
System.out.println("[" + accountId + "] Transaction Approved for " + name + ". Remaining: $" + balance);
} else {
System.out.println("[" + accountId + "] Transaction Denied for " + name + ". Insufficient funds.");
}
}
}
public static void main(String[] args) {
// 4. The shared resource
BankAccount sharedAccount = new BankAccount("SAV-2026-0112", 100);
// 5. Define the withdrawal task with random delay
Runnable withdrawalTask = () -> {
// Random delay (0-50ms) to ensure fair chance for both threads
try { Thread.sleep((long) (Math.random() * 50)); } catch (InterruptedException e) { }
sharedAccount.withdraw(80);
};
// 6. Create threads sharing the same task
Thread alice = new Thread(withdrawalTask, "Alice");
Thread bob = new Thread(withdrawalTask, "Bob");
// 7. Start threads
alice.start();
bob.start();
try {
// 8. Wait for completion
alice.join();
bob.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final Account Balance: $" + sharedAccount.balance);
}
}