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

Thread-local variables in Java (ctd)

Example: using ThreadLocal to re-use Calendar objects

So when in practice would we use ThreadLocal? A typical example of using ThreadLocal would be as an alternative to an object or resource pool, when we don't mind creating one object per thread. Let's consider the example of a pool of Calendar instances. In an application that does a lot of date manipulation, Calendar classes may be a good candidates for pooling because:

  • Creating a Calendar is non-trivial (various calculations and accesses to localisation resources need to be made each time one is created);
  • There's no actual requirement to share Calendars between threads or have fewer calendars than threads.

One (inefficient) way to re-use Calendars would be to create a 'calendar pool' class such as this:

public class CalendarFactory {
  private List calendars = new ArrayList();
  private static CalendarFactory instance = new CalendarFactory();

  public static CalendarFactory getFactory() { return instance; }

  public Calendar getCalendar() {
    synchronized (calendars) {
      if (calendars.isEmpty()) {
        return new GregorianCalendar();
      } else {
        return calendars.remove(calendars.size()-1);
      }
    }
  }
  public void returnCalendar(Calendar cal) {
    synchronized (calendars) {
      calendars.add(cal);
    }
  }
  // Don't let outsiders create new factories directly
  private CalendarFactory() {}
}

Then a client could call:

Calendar cal = CalendarFactory.getFactory().getCalendar();
try {
  // perform some calculation using cal
} finally {
  CalendarFactory.getFactory().returnCalendar(cal);
}

This would allow us to re-use Calendar objects but it's a bit inefficient because in each case it makes two synchronized calls when we're actually not that bothered about sharing the calendars across the threads. We wouldn't care, for example, if Thread 1 created and finished with a calendar and then Thread 2 created another, even though Thread 1 had finished with its. The number of threads will typically be small-ish (maybe in the hundreds at the very most), and so having as many calendars knocking around as there are threads is an OK compromise. (This would not be the case, for example, with database connections: if possible, we generally would want Thread 2 to use the connection that Thread 1 had just finished with rather than creating another; in such cases, thread-local variables aren't the right solution.) Now let's see how we can improve CalendarFactory using ThreadLocal:

public class CalendarFactory {
  private ThreadLocal<Calendar> calendarRef = new ThreadLocal<Calendar>() {
    protected Calendar initialValue() {
      return new GregorianCalendar();
    }
  };
  private static CalendarFactory instance = new CalendarFactory();

  public static CalendarFactory getFactory() { return instance; }

  public Calendar getCalendar() {
    return calendarRef.get();
  }

  // Don't let outsiders create new factories directly
  private CalendarFactory() {}
}

Note that there is still a single, static instance of CalendarFactory shared by all threads. But that single instance uses the ThreadLocal variable calendarRef, which has a per-thread value. Inside getCalendar(), the call to calendarRef.get() will always operate on our thread-private "instance" of the variable, and we don't need any synchronization.

This example uses the Java 5 generics feature: we declare ThreadLocal as 'containing' Calendar objects, so that the subsequent get() method doesn't need an explicit cast. (That is, the cast is inserted automatically by the compiler.) Another feature of this example is the initialValue() method. We actually subclass ThreadLocal and override initialValue() to provide an appropriate object each time a new one is required (i.e. when get() is called for the first time on a particular thread). We could of course have simply checked for null inside the getCalendar() method (the first time get() is called on a ThreadLocal for a particular thread, it returns null) and set the value if it was null:

  public Calendar getCalendar() {
    Calendar cal = calendarRef.get();
    if (cal == null) {
      calendarRef.set(cal = new GregorianCalendar());
    }
    return cal;
  }

However, overriding ThreadLocal.initialValue() automatically handles this logic and makes our code a bit neater– especially if we're calling get() in multiple places.

Note that we don't have a "return to pool" method. Once created, we let the calendar hang around for as long as the thread is alive1. If we really wanted to remove these instances at a particular moment, then as of Java 5, we can call calendarRef.remove(), which removes the calling thread's value; it would be set to the initial value again on the next call to calendarRef.get().

Next...

On the next page, we look at general criteria for deciding when to use ThreadLocal.

Notes
1. All values set on a ThreadLocal also become garbage collectable if the ThreadLocal becomes no longer reachable outside of the Thread class. In other words, a thread's map of ThreadLocal to value holds on to the ThreadLocal only via a weak reference.

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 2015. All rights reserved.