Every object has a lock. At any moment, that lock is controlled by, at most,
one single thread. The lock controls access to the object’s synchronized code.
A thread that wants to execute an object’s synchronized code must first
attempt to acquire that object’s lock. If the lock is available―that is, if it is
not already controlled by another thread―then all is well. If the lock is under
another thread’s control, then the attempting thread goes into the Seeking
Lock state and only becomes ready when the lock becomes available. When
a thread that owns a lock passes out of the synchronized code, the thread
automatically gives up the lock. All this lock-checking and state-changing is
done behind the scenes; the only explicit programming you need to do is to
declare code to be synchronized.