Most of you must be knowing what we mean by Automicity in programming. To put it in simple – either a set of things happens/executes completely or not at all.So how does we achieve Automicity in a multi-threaded programming?
The obvious answer is sychronization. But just using sychronization is enough? Consider the following program
public void waitTillCompletion(){
while(isNotEmpty()){ // Line 1
waitForNotification(); // Line 2
}
}
private sycchronized boolean isNotEmpty(){
return !queue.isEmpty();
}
private sycchronized void waitForNotification(){
myThread.wait(); // ignoring exception handling for brefing
}
This is general programming pattern where we are making a thread to wait till some condition is satisfied. The two private methods which actually does the job of checking for the condition and making the thread wait are syncrhonized. We were having such a code in our application and was working fine but lately it was observed that sometimes the thread enters into wait state and remains there forever. Do you see anything wrong with this program? It works most of the time but its not a thread-safe program, I will tell you why.
Consider a thread T1 executes waitTillCompletion() and enters Line 1. It executes isNotEmpty() method in a synchronized context and comes out with the result say true. Now it returns the lock and it means that any other thread can acquire the lock. Say another thread T2 acquired the lock, made the queue empty and notified other threads. Thats it, its job is done with sending the notification. Now T1 executes Line 1 and can again acquire the lock to execute waitForNotification(). It enters into the wait() state forever and remain there forever as T2 has already sent the notification.
So what went wrong here? The check for emptiness and wait are not atomic. There is a chance that after checking for emptiness and before entering into wait state the emptiness condition can change. How do we solve this problem? Make the entire waitTillCompletion() method synchronized and thereby making the two operations atomic.
Note: Here I have explained using method synchronization for easy understanding, its always better to use a lock object and synchronize on that.