Synchronization under the hood, and why Java 5 improves it

On the previous page, we saw that the synchronized keyword provides some benefits, including simple implementation for the programmer without getting bogged down in how the JVM actually implements synchronization. But we saw that this "black box" approach has some potential disadvantages. To understand when and how Java 5 improves this situation, we need to delve "under the hood" of synchronization for a minute.

Let's consider the following program, which maintains a thread-safe counter:

public class Counter {
  private int count;
  public synchronized int getCount()        { return count; }
  public synchronized void incrementCount() { count++; }
}

Now, let's consider what a JVM implementation might have to do under the hood when we enter one of the synchronized methods such as incrementCount(). Recall that entering and exiting a synchronized block actually means (among other things) acquiring and releasing a lock on the object being synchronized on (in this case, the Counter instance that the method is being called on). For every Java object, the JVM must therefore hold information at least on which thread (if any) currently has access to the lock and, how many times that thread has acquired the lock. We'll see in a moment that it in some cases it may need to hold a pointer to an operating system lock object plus potentially other information. The JVM needs to:

All of the above actions must occur atomically. It's no good if we read the thread owner and see that no thread owns the lock, but before we have chance to set our thread ID and update the lock count, another thread intervenes, reads the zero thread owner, and also thinks it can take the lock. So at a very low level, we need to synchronize on this "lock housekeeping" data.

On the next page, we look at this low-level means of synchronization.