Home  Synchronization and concurrency  wait/notify  final  volatile  synchronized keyword  Java threading  Deadlock (and avoiding it)  Java 5: ConcurrentHashMap  Atomic variables  Explicit locks  Queues  Semaphores  CountDownLatch  CyclicBarrier

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:

  • check the variable telling it which thread owns the lock on the object;
  • if no thread owns the lock, mark the variable as being the current thread's ID, and set the lock count to one;
  • if the current thread owns the lock, increase the lock count;
  • if a different thread owns the lock, we can't proceed for now and need to somehow wait for the lock to become available. Many operating systems provide a "native" means to wait for other threads via low-level lock objects, so a possible implementation is to create/wait for one of these.

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.

Article written by Neil Coffey (@BitterCoffey).

Software

 LetterMeister (word puzzle game for iPhone)
 Currency Quoter (currency converter/predictor)
 French Vocab Games for iPhone/iPad
 Vocabularium: create Spanish vocab podcasts


Java programming articles and tutorials on this site are written by Neil Coffey (@BitterCoffey). Suggestions are always welcome if you wish to suggest topics for Java tutorials or programming articles, or if you simply have a programming question that you would like to see answered on this site. Most topics will be considered. But in particular, the site aims to provide tutorials and information on topics that aren't well covered elsewhere, or on Java performance information that is poorly described or understood. Suggestions may be made via the Javamex blog (see the site's front page for details).
Copyright © Neil Coffey 2014. All rights reserved.