Synchronized Usage - Bank Account Concrete Example

In many scenarios, especially when working with shared data across multiple threads, synchronization is crucial to avoid concurrency issues such as race conditions.

A classic example is managing a shared bank account where multiple threads try to update the account balance.

Without synchronization, it's possible for two or more threads to modify the balance at the same time, leading to incorrect or inconsistent results.

BankAccount.java
public class BankAccount {
    private int balance;
 
    public BankAccount(int initialBalance) {
        this.balance = initialBalance;
    }
 
    // Synchronized method to add money into the account
    public synchronized void deposit(int amount) {
        balance += amount;
        System.out.println("Deposited: " + amount + ", Balance: " + balance);
    }
 
    // Synchronized method to withdraw money from the account
    public synchronized void withdraw(int amount) {
        if (balance >= amount) {
            balance -= amount;
            System.out.println("Withdrawn: " + amount + ", Balance: " + balance);
        } else {
            System.out.println("Insufficient funds for withdrawal: " + amount);
        }
    }
 
    public static void main(String[] args) {
        BankAccount account = new BankAccount(1000);
 
        // Crearea și pornirea thread-urilor pentru depozit și retragere
        // Creating and starting the threads for deposit and withdrawal
        Thread depositThread = new Thread(() -> account.deposit(500));
        Thread withdrawThread = new Thread(() -> account.withdraw(800));
 
        depositThread.start();
        withdrawThread.start();
    }
}

The BankAccount Class: This class represents a simple bank account with deposit and withdrawal operations.

Synchronized Methods: The deposit and withdraw methods are synchronized. This ensures that at any given time, only one thread can execute one of these methods on a specific instance of BankAccount. This prevents race conditions, where the account balance might be updated incorrectly if multiple threads try to modify the balance at the same time.

Threads for Deposit and Withdrawal: In the main method, two threads are created: one for depositing money and the other for withdrawing money.

By using synchronized on the deposit and withdraw methods, we ensure that changes to the account balance are atomic and thread-safe. In other words, when one thread is executing one of these methods, no other thread can execute the same method (or any other synchronized method) on the same account instance at the same time. This prevents scenarios where two threads might, for example, withdraw money at the same time, leading to a negative balance that should not be possible in a real bank account.

This pattern is fundamental in multithreaded programming and is crucial for maintaining data consistency and preventing errors in applications involving concurrent access to shared resources.