Today at work we found a subtle issue that will sometimes break your code in very difficult to find ways. Read this if you don’t like days of bug hunting for mysterious issues only occurring on high-load production machines.
Ever wrote code like this?
private static final DateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd");
Most of us did. It’s the most intuitive way to use a DateFormat to format some Date object as a human-readable String.
Unfortunately, it’s wrong.
When used in a multi-threaded context (e.g. in a Servlet), this will end up breaking. Sometimes. When you least expect it.
Why sharing Formats across threads is wrong
The reason why it’s wrong is hidden in the Javadocs of java.text.Format:
“Formats are generally not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.”
Imho it’s a design flaw in Java that Format instances aren’t immutable. Hardly anyone ever changes the format settings of a Format at runtime, but many people get bitten by difficult to reproduce bugs because they are inadvertently sharing a mutable, non-synchronized instance of a Format across multiple threads, for example when using Formats in Servlets.
ThreadLocal to the rescue
Because Format’s can be relatively expensive to create, as an alternative to recreating it each time it’s used, you can put it inside a ThreadLocal:
private static final ThreadLocal FORMAT = new ThreadLocal()
{
@Override protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyy-MM-dd");
}
};
Then use it like this:
FORMAT.get().format(new Date());
Why this works
ThreadLocal is a ‘magical’ class that keeps a separate version of the field it’s wrapping for each thread that is running the code. The field becomes local to the thread. Read it’s Javadocs.
Share your thoughts
Ever got hurt by this? How did you solve it? Leave a comment below.