Executing Synchronized Code
Threads share the same memory space—that is, they can share resources. However, there are critical situations where it is desirable that only one thread at a time has access to a shared resource. For example, crediting and debiting a shared bank account concurrently among several users without proper discipline will jeopardize the integrity of the account data. Java provides the concept of synchronization in order to control access to shared resources. It is one of the mechanisms that Java provides to write thread-safe code (§23.4, p. 1451).
The keyword synchronized and the intrinsic lock mechanism (p. 1380) form the basis for implementing synchronized execution of code. We refer to this as intrinsic locking (also referred to as built-in synchronization). There are two ways in which execution of code can be synchronized: by declaring synchronized methods or by using synchronized statements. The code that is synchronized to execute by one thread at a time is often referred to as a critical section.
Acquiring the Object Lock
In order to execute synchronized code, a thread must first acquire the lock of the relevant object, as illustrated in Figure 22.7. If this lock is not available, the thread transits from the RUNNING substate to the BLOCKED state. The thread is put in the entry set of the object. Threads in the BLOCKED state are grouped according to the object whose lock they are waiting for. The getState() method on a blocked thread in this state will return the value Thread.State.BLOCKED.
If there are several threads waiting for the same object lock, one is chosen at the discretion of the thread scheduler. It then transits to the READY substate where it takes its turn to run again.
Example 22.4 illustrates acquiring the lock in order to synchronize on a shared resource.
Figure 22.7 Blocked for Lock Acquisition