我们正面临着TimeZone::getTimeZone(String)完全成为瓶颈的重大性能问题。它正在获取类本身的锁(因为该方法是静态的),并且目前几乎所有的执行线程都在等待获取这个锁。
我想出了下面的解决方案。这被证明是一个很大的提升性能。
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();
}
问题我有:是否有人知道它是安全的缓存TimeZone类之外的TimeZone实例?这意味着TimeZone/ZoneInfo/ZoneInfoFile做了一些魔术来内部更新其缓存,以便我在这里的应用程序缓存与TimeZone内的缓存不一致。
在有人建议之前-这不是升级到JDK 8日期/时间API的选项,也不是Joda时间。
在有人抱怨之前:-)-我知道通常不建议重复检查。
一旦JVM从文件中加载了时区,它们就固定了。检查Oracle的时区更新工具:http://www.oracle.com/technetwork/java/javase/tzupdater自述- 136440. - html
您需要启动JVM来应用更新。
请注意,缓存将是您的目的的缓存。其他一些库仍然可能(重新)使用慢路径加载TimeZone。
但这里有一个很大的警告:你不会有什么收获。HotSpot实现已经缓存了:
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/sun/util/calendar/ZoneInfoFile.java/# 125
调用栈为:
- ZoneInfoFile:: getZoneInfo0(字符串)
- ZoneInfoFile:: getZoneInfo(字符串)
- ZoneInfo:: getTimeZone(字符串)
- 时区::getTimeZone(字符串)
因为返回的Zone是可变的,所以它是一个防御性复制。
应该可以。以下是一些建议:
- 可以使用并发hashmap
- 或者你可以使用Guava (https://code.google.com/p/guava-libraries/wiki/CachesExplained)
- 我以前试过这个,我使用了1天的TTL缓存条目。每天缓存刷新(延迟加载)。