Why is synchronization required in Java? Please give a relevant example to explain.
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.
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();
}
}
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.
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;
}
}
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.
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.