为什么Java的SimpleDateFormat不是线程安全的?

9 浏览
0 Comments

为什么Java的SimpleDateFormat不是线程安全的?

请用示例代码解释SimpleDateFormat为什么不是线程安全的。这个类有什么问题?\n问题出现在SimpleDateFormat的format函数上。\n请给出一个示例代码来演示这个类的问题。\nFastDateFormat是线程安全的。为什么?\nSimpleDateFormat和FastDateFormat有什么区别?\n请解释并给出一个示例代码来演示这个问题。

0
0 Comments

在Java中,SimpleDateFormat类在多线程环境下不是线程安全的。这意味着在多个线程同时使用同一个SimpleDateFormat实例进行日期格式化操作时,可能会导致结果不正确。

问题的出现原因是SimpleDateFormat类的实例是可变的,而且内部使用了共享的Calendar实例来进行日期计算和格式化。由于多个线程可以同时访问和修改这个共享的Calendar实例,就会导致竞争条件的发生。这可能会导致日期格式化的结果不一致或不正确。

为了解决这个问题,可以使用Java 8中引入的DateTimeFormatter类来替代SimpleDateFormat。DateTimeFormatter是不可变的,线程安全的类,可以确保在多线程环境下正确地进行日期格式化操作。

使用DateTimeFormatter时,需要注意的是,它使用了新的日期时间类(如LocalDate、LocalDateTime等)来代替旧的Date类。这是一个优点,因为新的日期时间类更加易于使用,而且提供了更多的功能。

此外,还需要注意的是,SimpleDateFormat在处理时区偏移量(offset)时可能会出现问题。这是因为SimpleDateFormat使用了默认的时区设置,而DateTimeFormatter可以明确指定时区。因此,在使用DateTimeFormatter时,需要确保正确地设置时区,以避免偏移量的问题。

为了在多线程环境下进行日期格式化操作,应该使用Java 8中的DateTimeFormatter类来替代SimpleDateFormat。DateTimeFormatter是线程安全的,能够确保在多线程环境下正确地进行日期格式化操作。同时,需要注意设置正确的时区,以避免偏移量的问题。

0
0 Comments

Java的SimpleDateFormat类用于以区域敏感的方式格式化和解析日期。然而,SimpleDateFormat类并不是线程安全的,官方文档中强烈建议为每个线程创建单独的格式实例,或者在并发访问时进行外部同步。

为了使SimpleDateFormat类线程安全,可以采取以下几种方法:

1. 每次使用时创建一个新的SimpleDateFormat实例。虽然这种方法是线程安全的,但是速度最慢。

2. 使用同步机制。这种方法并不推荐,因为在服务器上使用同步可能会导致线程阻塞。

3. 使用ThreadLocal。这是最快的方法,将SimpleDateFormat实例保存在ThreadLocal变量中,每个线程只能访问自己的实例。

作者对第二种方法持不同意见,认为在服务器上使用同步并不会成为性能瓶颈。但是,有人使用自定义的Web框架,在控制器中包装了同步块,导致所有访问都被串行化,这样可能会导致性能问题。

作者认为,应该先采用最简单的方法:每次需要时创建一个新的SimpleDateFormat实例。只有在出现性能问题时,才考虑使用共享实例。因为任何在线程之间共享的东西都可能导致潜在的竞态条件。

此外,还提到了一个问题,如果一个线程在SimpleDateFormat的方法中出现问题导致阻塞,那么所有其他线程也将被阻塞。

总之,SimpleDateFormat类不是线程安全的,为了避免并发访问的问题,推荐为每个线程创建单独的格式实例或使用ThreadLocal变量保存实例。

0
0 Comments

Java的SimpleDateFormat类不是线程安全的,这是因为它在实例字段中存储了中间结果。如果一个实例被两个线程使用,它们可能会干扰彼此的结果。

观察源代码可以发现,SimpleDateFormat类中有一个Calendar实例字段,用于DateFormat和SimpleDateFormat的操作。例如,parse()方法首先调用calendar.clear(),然后调用calendar.add()。如果另一个线程在第一个调用完成之前调用parse(),它会清除calendar,但是其他调用将期望calendar被填充中间结果的计算。

解决方法之一是将日期格式放在ThreadLocal中以避免线程安全问题。一些库就是这样使用的。如果需要在同一个线程中多次使用相同的格式,则可以这样做。但是,如果使用了一个servlet容器(它有一个线程池),请记得在完成后清除线程本地变量。

老实说,我不明白他们为什么需要实例字段,但事实就是这样。您也可以使用线程安全的joda-time的DateTimeFormat。

这个问题在JDK8中是否修复了?如果没有,为什么?

这个问题在JDK8中没有被修复,但是JDK8引入了新的java.time包,其中包括线程安全的DateTimeFormatter。

它永远无法“修复”,而不破坏向后兼容性。最好将其保持不变,并让新代码使用较新的线程安全的替代品。

实际上,它很容易被修复,因为它是实现细节…

它不是一个细节,因为它从根本上设计得很糟糕。而且完全重写它也太冒险了。

如果不改变接口…

理论上,是的,那样不会破坏任何东西。但实践中,我保证在过去几十年(是的,字面上几十年,SimpleDateFormat从1.1版本开始,这是在1996年发布的,已经有20多年了)中,有很多人依赖于缺乏线程安全来实现某些功能。这是可怕的做法吗?是的,显然是。这是否意味着它不会发生?我希望不会。

缺乏线程安全只会导致不可预测的错误偶尔出现(当以不安全的方式使用时),很难想象一个程序会“依赖”于这一点。

仅在您发表评论的一年中,我就见过依赖于更糟糕行为的程序。我保证你,某个地方,某个人依赖于它。也许作为一种随机性的来源;也许作为一种检测变化的不好方法,我不知道。再次强调,这不是一个好的实践。但这从来没有阻止过任何人。(多处理问题并不是真正“不可预测”的,它们只是复杂的。你可以这样做,但不要这样做。)

我的人性信仰破灭了 :/

0