java.util.TimeZone.getTimeZone(id)
是一种基于id获取时区的方法。当我使用该类时,我用反编译器打开它,发现它是synchronized
。由于它是static
,这意味着没有两个线程可以同时调用该方法。如果多个线程(例如在Web应用程序中)经常获得时区,这可能会非常痛苦。为什么必须同步?
然后我意识到文档没有说同步。所以,我的反编译器可能是错误的。然后我打开了the source,它被同步了。为什么没有记录?我知道javadoc不包含synchronized
关键字,但可以提及它。
当然,解决方案是使用joda-time DateTimeZone
答案 0 :(得分:7)
该方法最终可以实际创建TimeZone
(遵循代码)并将其添加到Map
。我猜大家都认为这不是一个你应该经常打电话的方法,并且很容易解决。
我很难想出一个合法的案例,其中synchronized
会被争议。无争议的synchronized
(即使在非常高性能的情况下,这是我经常使用的东西)也很便宜。
要获得争用,您不仅需要多个线程,还需要同时触及此特定代码块的许多线程。如果您在这种情况下遇到问题,可以轻松地将自己的缓存保存在ConcurrentHashMap
或完全解锁的结构中。
至于为什么没有记录 - 同步是实现的属性。欢迎您实现不执行此同步的备用库。 JDK文档记录了Java库,而不是(大部分)Sunacle的实现。
答案 1 :(得分:6)
我们看到了同样的问题,阻塞了TimeZone中的线程。它看起来像2011年11月引入的回归,见http://hg.openjdk.java.net/jdk6/jdk6/jdk/annotate/dd8956e41b89/src/share/classes/java/util/TimeZone.java。以前TimeZone使用了InheritableThreadLocal作为Map。 TimeZone函数现在由SecurityManager控制(在AppContext类下具有同步的HashMap)。以下函数受到影响:Date.normalize(由toString,getTime和一堆不推荐的方法调用),Calendar.getInstance(Locale是参数的那个除外)。 http://coffeedriven.org/2012/10/14/be-carefull-with-calendar-getinstance-and-timezone-gettimezone也指同一问题。
VisualVM的堆栈跟踪(JDK6.45):
java.lang.Thread.State: BLOCKED (on object monitor)
at sun.awt.AppContext.get(AppContext.java:572)
- waiting to lock <0x0000000705490070> (a java.util.HashMap)
at sun.awt.AppContext$6.get(AppContext.java:774)
at java.util.TimeZone.getDefaultInAppContext(TimeZone.java:637)
at java.util.TimeZone.getDefaultRef(TimeZone.java:523)
at java.util.Date.normalize(Date.java:1176)
at java.util.Date.toString(Date.java:1010)
我打算在此向Oracle提交错误报告,我只需要重新创建一个小测试用例,以便通过建议的代码修复来清楚地演示问题。
JodaTime文档说明: Joda-Time在操作期间也分配了很少的临时对象,并且几乎不执行任何线程同步。在大量多线程或使用大量内存的系统中,Calendar,SimpleDateFormat和TimeZone可能成为瓶颈。当使用Joda-Time类时,瓶颈就会消失。
解决方法是使用JodaTime库,或修补JDK TimeZone类,或等待Oracle修复问题。