正在缓存 JDK 个 TimeZone 实例
Caching JDK TimeZone instances
我们面临着严重的性能问题,TimeZone::getTimeZone(String) 是一个完全的瓶颈。它正在锁定 class 本身(因为该方法是静态的),目前几乎所有执行线程都在等待获取此锁。
我想到了以下解决方案。它被证明对性能有很大的提升。
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 class 之外缓存 TimeZone 实例是否安全?这意味着 TimeZone/ZoneInfo/ZoneInfoFile 做了一些神奇的事情来内部更新它的缓存,这样我这里的应用程序缓存就与 TimeZone 内部的缓存不一致了。
并且在有人建议之前 - 升级到 JDK 8 Date/time API 和 Joda time 都不是一个选项。
在有人抱怨之前:-) - 我知道通常不推荐使用双重检查成语。
这应该有效。不过有一些建议:
- 您可以使用并发哈希图
- 或者您可以使用 Guava (https://code.google.com/p/guava-libraries/wiki/CachesExplained)
- 我以前试过这个,我为缓存条目使用了 1 天的 TTL。缓存每天都会刷新(延迟加载)。
一旦 JVM 从文件中加载时区,它们就会固定下来。
检查 Oracle 的时区更新程序实用程序:
http://www.oracle.com/technetwork/java/javase/tzupdater-readme-136440.html
您需要反弹 JVM 才能应用更新。
请注意,缓存将是您的目的的缓存。其他一些库仍然可能(重新)使用慢速路径加载 TimeZone。
但这里有一个重要的警告:
你不会有太多收获。 HotSpot 实现已经缓存:
调用堆栈为:
- ZoneInfoFile::getZoneInfo0(字符串)
- 区域信息文件::getZoneInfo(字符串)
- 区域信息::getTimeZone(字符串)
- 时区::getTimeZone(字符串)
因为返回的 Zone 是可变的,所以它是一个防御副本。
我们面临着严重的性能问题,TimeZone::getTimeZone(String) 是一个完全的瓶颈。它正在锁定 class 本身(因为该方法是静态的),目前几乎所有执行线程都在等待获取此锁。
我想到了以下解决方案。它被证明对性能有很大的提升。
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 class 之外缓存 TimeZone 实例是否安全?这意味着 TimeZone/ZoneInfo/ZoneInfoFile 做了一些神奇的事情来内部更新它的缓存,这样我这里的应用程序缓存就与 TimeZone 内部的缓存不一致了。
并且在有人建议之前 - 升级到 JDK 8 Date/time API 和 Joda time 都不是一个选项。
在有人抱怨之前:-) - 我知道通常不推荐使用双重检查成语。
这应该有效。不过有一些建议:
- 您可以使用并发哈希图
- 或者您可以使用 Guava (https://code.google.com/p/guava-libraries/wiki/CachesExplained)
- 我以前试过这个,我为缓存条目使用了 1 天的 TTL。缓存每天都会刷新(延迟加载)。
一旦 JVM 从文件中加载时区,它们就会固定下来。 检查 Oracle 的时区更新程序实用程序: http://www.oracle.com/technetwork/java/javase/tzupdater-readme-136440.html
您需要反弹 JVM 才能应用更新。
请注意,缓存将是您的目的的缓存。其他一些库仍然可能(重新)使用慢速路径加载 TimeZone。
但这里有一个重要的警告: 你不会有太多收获。 HotSpot 实现已经缓存:
调用堆栈为:
- ZoneInfoFile::getZoneInfo0(字符串)
- 区域信息文件::getZoneInfo(字符串)
- 区域信息::getTimeZone(字符串)
- 时区::getTimeZone(字符串)
因为返回的 Zone 是可变的,所以它是一个防御副本。