Class 13: Synchronization with synchronized Methods
Learn how Java helps us protect shared data when multiple threads try to update it at the same time.
1. The Problem We Saw in the Previous Class
In our previous class, we learned about race conditions. We saw how multiple threads updating the same counter could produce an incorrect result.
Let's quickly revisit that example.
class Counter {
int count = 0;
void increment() {
count++;
}
}
Imagine two threads calling the increment()
method at the same time.
Both threads may read the same value, increase it, and write their results back. One update can overwrite the other.
This is how we can end up with an incorrect counter value.
So, how do we prevent this?
2. What Is Synchronization?
Synchronization is a way of controlling access to shared data when multiple threads are involved.
Think about a small room with only one key.
If one person is using the room, another person must wait until the first person comes out and returns the key.
Only one person can use the room at a time.
Java provides a similar mechanism using the
synchronized keyword.
When a method is synchronized, only one thread at a time can execute that method on the same object.
3. Our First synchronized Method
Let's modify our previous Counter example.
class Counter {
int count = 0;
synchronized void increment() {
count++;
}
}
Notice the word synchronized before the method.
This small change makes a big difference.
When one thread enters the increment() method,
another thread trying to enter the same synchronized method
on the same Counter object must wait.
Once the first thread finishes executing the method, another thread can enter it.
In this way, the shared counter is protected from simultaneous updates through this method.
4. Understanding It with Two Threads
Let's see how two threads work with our synchronized method.
class Counter {
int count = 0;
synchronized void increment() {
count++;
}
}
public class Main {
public static void main(String[] args)
throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 100000; i++) {
counter.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 100000; i++) {
counter.increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.count);
}
}
Let's understand what is happening.
- We create one Counter object.
- Both threads use the same Counter object.
- Each thread calls
increment()100,000 times. - The method is synchronized, so only one thread at a time can execute it on this object.
- We use
join()to wait for both threads to finish.
The expected output is:
200000
Every increment is now protected from another thread executing this same synchronized method on the same object at the same time.
5. What Happens When Another Thread Tries to Enter?
Every Java object has an associated monitor, which we can think of as a lock.
When a thread enters a synchronized instance method, it acquires the lock associated with that object.
If another thread tries to enter a synchronized instance method on the same object, it must wait until the lock becomes available.
Once the first thread leaves the synchronized method, it releases the lock. Another waiting thread can then acquire it.
For now, remember this simple rule:
A synchronized instance method allows only one thread at a time to execute synchronized instance methods using the same object.
6. Important: Synchronization Is Based on the Object
Let's understand one important point.
Consider this example:
Counter counter1 = new Counter();
Counter counter2 = new Counter();
We now have two different Counter objects.
Each object has its own lock.
Therefore, one thread can execute a synchronized method
on counter1, while another thread can
simultaneously execute a synchronized method on
counter2.
They do not need to wait for each other because they are working with different objects and their respective locks.
This is an important distinction. Synchronization on one object does not automatically block access to every other object of the same class.
7. Can We Synchronize More Than One Method?
Yes. We can have multiple synchronized methods in the same class.
class BankAccount {
private int balance = 1000;
synchronized void deposit(int amount) {
balance = balance + amount;
}
synchronized void withdraw(int amount) {
balance = balance - amount;
}
synchronized int getBalance() {
return balance;
}
}
All three methods use the same object's lock when called on the same BankAccount object.
So, if one thread is executing deposit(),
another thread calling withdraw() on that
same object must wait.
This helps us protect the shared balance from concurrent access through these methods.
8. A Few Things to Remember
synchronizedhelps control access to shared data.- Only one thread at a time can hold a particular object's monitor lock.
- A synchronized instance method uses the lock of the current object.
- Different objects have different instance locks.
- When a thread leaves a synchronized method, it releases the lock.
- Synchronization works only when the relevant access to shared data is properly coordinated. Making one method synchronized does not automatically protect every other way of accessing that data.
Test Your Understanding
Let's see how well you understood this class!
Question 1: Why do we use the synchronized keyword?
Question 2: What happens when one thread enters a synchronized instance method?
Question 3: Two threads call the same synchronized instance method on the same object. What happens?
Question 4: Do two different Counter objects share the same instance lock?
Question 5: When is the object's lock released after a synchronized method executes?
Class Summary
Let's quickly revise what we learned in this class.
- Synchronization helps protect shared data when multiple threads access it.
- We use the
synchronizedkeyword to make a method synchronized. - A synchronized instance method uses the lock associated with the current object.
- Only one thread at a time can hold that object's monitor lock.
- Different objects have different instance locks.
- The lock is released when the thread leaves the synchronized method.
Remember: Synchronization helps us control which thread can access shared data at a particular time.
You have completed 13 out of 46 classes.