Importance of Synchronization in Java with Banking Example

Répondu en utilisant GPT-4o-mini par Java Q&A Hub

Question :

Why is synchronization required in Java? Please give a relevant example to explain.

Réponse :

Synchronization in Java is required to control access to shared resources by multiple threads. When multiple threads access shared data concurrently, it can lead to inconsistent or unpredictable results, known as race conditions. Synchronization ensures that only one thread can access a resource at a time, preventing data corruption and ensuring thread safety.

Example of Synchronization

Consider a simple banking application where multiple threads are trying to update the balance of a bank account. Without synchronization, two threads could read the balance at the same time, update it, and write it back, leading to incorrect results.

Here’s a simple example to illustrate this:

class BankAccount {
    private int balance = 1000;

    public int getBalance() {
        return balance;
    }

    // Method to withdraw money
    public void withdraw(int amount) {
        if (balance >= amount) {
            // Simulate a delay
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            balance -= amount;
        }
    }
}

class WithdrawThread extends Thread {
    private BankAccount account;
    private int amount;

    public WithdrawThread(BankAccount account, int amount) {
        this.account = account;
        this.amount = amount;
    }

    @Override
    public void run() {
        account.withdraw(amount);
        System.out.println("Withdrawn: " + amount + ", Remaining Balance: " + account.getBalance());
    }
}

public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount();

        // Creating multiple threads trying to withdraw money
        WithdrawThread t1 = new WithdrawThread(account, 700);
        WithdrawThread t2 = new WithdrawThread(account, 500);

        t1.start();
        t2.start();
    }
}

Potential Issue Without Synchronization

In the above code, if both threads t1 and t2 run concurrently, they might both check the balance at the same time and see that it is sufficient to withdraw their respective amounts. This could lead to a situation where the balance goes negative, which is incorrect.

Solution with Synchronization

To fix this, we can synchronize the withdraw method:

public synchronized void withdraw(int amount) {
    if (balance >= amount) {
        // Simulate a delay
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        balance -= amount;
    }
}

Explanation of the Solution

By adding the synchronized keyword to the withdraw method, we ensure that only one thread can execute this method at a time for a given instance of BankAccount. If one thread is executing the withdraw method, any other thread that tries to call this method will be blocked until the first thread finishes its execution. This prevents race conditions and ensures that the balance is updated correctly.

Conclusion

Synchronization is crucial in multi-threaded programming to ensure data integrity and consistency when multiple threads access shared resources. It helps prevent issues like race conditions, ensuring that the application behaves as expected.