Before we try to understand what the Synchronized Keyword does, lets try to understand what a Race Condition is:
Lets say you have a class that has a variable “var1”. You have two threads – Thread 1 and Thread 2. Thread 1 is going to wake up every 5 seconds and display the value in this variable on screen. Whereas, Thread 2 is going to wake up every 15 seconds and ask the user for inputs on the value he wishes to see on screen. Both Thread 1 and Thread 2 are working on the same data “var1” here.
Did you guess the problem here?
Lets say you are the user and the system has beeped you asking for the text. If you take more than 5 seconds to enter the value, thread 1 is going to pick up the old value in “var1” and try to display it on screen while you are trying to update this “var1” with your input.
This is a race condition – a situation wherein one thread tries to read a data that is being concurrently modified by another thread.
A race condition exists because these two methods are not atomic.
What is Atomic?
The term atomic is related to the atom, once considered the smallest possible unit of matter, unable to be broken into separate parts. When computer code is considered atomic, it cannot be interrupted during its execution. This can either be accomplished in hardware or simulated in software. Generally, atomic instructions are provided in hardware and are used to implement atomic methods in software.
In our case, we define atomic code as code that can't be found in an intermediate state. In our example, it means that when one thread is trying to read the data for display on screen, the other thread wouldn't be allowed to update or edit it. At no point of time would either threads be working on data that is not consistent.
The Java specification provides certain mechanisms that deal specifically with this problem. The Java language provides the synchronized keyword especially and exclusively for this purpose only. This keyword allows the programmer access to a resource that is very similar to a mutex lock. For our purposes, it simply prevents two or more threads from calling the methods of the same object at the same time.
What is a Mutex Lock?
A mutex lock is also known as a mutually exclusive lock. This type of lock is provided by many threading systems as a means of synchronization. Only one thread can grab a mutex at a time: if two threads try to grab a mutex, only one succeeds. The other thread has to wait until the first thread releases the lock before it can grab the lock and continue operation.
In Java, every object has an associated lock. When a method is declared synchronized, the executing thread must grab the lock associated with the object before it can continue. Upon completion of the method, the lock is automatically released.
By declaring our two methods that display data on screen and the one that reads user input synchronized, we eliminate the race condition. If one thread wants to call one of these methods while another thread is already executing one of them, the second thread must wait: the first thread gets to complete execution of its method before the second thread can execute its method. Since only one thread gets to call either method at a time, only one thread at a time accesses the data.
Under the covers, the concept of synchronization is simple: when a method is declared synchronized, the thread that wants to execute the method must acquire a token, which we call a lock. Once the method has acquired (or checked out or grabbed) this lock, it executes the method and releases (or returns) the lock. No matter how the method returns; including via an exception, the lock is released. There is only one lock per object, so if two separate threads try to call synchronized methods of the same object, only one can execute the method immediately; the other has to wait until the first thread releases the lock before it can execute the method.
Previous: Determining the Current Thread
Next: The Volatile Keyword