Non-thread-safe code can cause race conditions, inconsistent data, and unpredictable behavior when multiple threads access shared resources simultaneously. To make such code thread-safe, synchronization should be applied to critical sections so that only one thread can modify shared data at a time while maintaining data consistency.
Key Points: • Use the synchronized keyword to protect shared mutable state from concurrent modifications. • Synchronize only the critical section instead of the entire method to reduce lock contention and improve performance. • Ensure all threads access shared resources through the same synchronization mechanism.
Example: Consider a banking application where multiple threads update the same account balance. Without synchronization, simultaneous deposits and withdrawals may produce incorrect results. Synchronizing the balance update ensures that one thread completes its operation before another thread modifies the same data.
Code Example:
public class BankAccount {
private double balance;
public void deposit(
double amount) {
synchronized (this) {
balance += amount;
}
}
public synchronized double getBalance() {
return balance;
}
}Before Refactoring:
public void deposit(
double amount) {
balance += amount;
}The above code is not thread-safe because multiple threads can update balance simultaneously.
After Refactoring:
public void deposit(
double amount) {
synchronized (this) {
balance += amount;
}
}This ensures that only one thread can execute the critical section at a time.
Interview Tip: A concise interview answer is: I identify the shared mutable resource and protect the critical section using synchronized methods or synchronized blocks. I keep the lock scope as small as possible to maintain thread safety while minimizing performance overhead caused by excessive locking.