We're facing significant performance issues with TimeZone::getTimeZone(String) being a complete bottleneck. It is taking a lock on the class itself (since the method is static), and currently almost all execution threads are waiting to acquire this lock.
I came up with the following solution. It is proven to be a great boost to performance.
private static final Object TIME_ZONE_CACHE_LOCK = new Object();
private static volatile Map<String, TimeZone> ourCachedTimeZones = new HashMap<>();
public static TimeZone getTimeZoneFor(String timeZoneId)
{
TimeZone timeZone = ourCachedTimeZones.get(timeZoneId);
if (timeZone == null)
{
TimeZone newTimeZone = TimeZone.getTimeZone(timeZoneId);
synchronized (TIME_ZONE_CACHE_LOCK)
{
timeZone = ourCachedTimeZones.get(timeZoneId);
if (timeZone == null)
{
timeZone = newTimeZone;
Map<String, TimeZone> cachedTimeZones = new HashMap<>(ourCachedTimeZones);
cachedTimeZones.put(timeZoneId, timeZone);
ourCachedTimeZones = cachedTimeZones;
}
}
}
// Clone is needed since TimeZone is not thread-safe
return (TimeZone) timeZone.clone();
}
The question I have: Does anyone know whether or not it is safe to cache TimeZone instances outside the TimeZone class? Meaning that TimeZone/ZoneInfo/ZoneInfoFile does some magic to internally update its cache so that the application cache I have here is not coherent with the one inside TimeZone.
And before someone suggests - it is not an option to upgrade to JDK 8 Date/time API, nor Joda time.
And before someone complains :-) - I know the double-check idiom is usually not recommended.