SimpleDateFormat.clone()线程安全吗?

时间:2018-04-24 09:25:19

标签: java multithreading thread-safety clone simpledateformat

我正在查看遗留代码并找到以下内容:

private static final SimpleDateFormat sdf = new SimpleDateFormat("...");
...
void foo() {
    bar(date, someMoreArgs, sdf.clone());
}

然后bar()继续使用传递的SimpleDateFormat格式化给定日期。

以上代码是否是线程安全的?如果多个线程同时调用sdf.clone(),其中一个克隆对象最终会被破坏吗?

我首先不会像自己那样编写代码。我知道有更好的方法可以做到这一点。但我不打算重构代码,除非它被证明不是线程安全的。

修改
更多信息需要澄清:

  1. 静态对象sdf本身从不用于格式化。它曾经使用的唯一操作是克隆。因此,我不希望其内容发生变化(除非克隆操作在对象内部写入一些瞬态数据)。

  2. 克隆从不被多个线程使用。

3 个答案:

答案 0 :(得分:1)

来自JavaDoc:

  

日期格式未同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问格式,则必须在外部进行同步。

所以我认为这取决于你如何使用该克隆,但它无法保证。克隆不会使您的类成为线程安全的。如果克隆对象没有在类实例之间共享,那么它应该没有问题,但我不建议这种方法。但是,你需要一个线程安全的日期格式化程序,我建议使用Apache Commons FastDateFormat,描述here

答案 1 :(得分:1)

基本上,clone()方法不会为您提供线程安全性。它只是将一个对象的属性复制到另一个对象。它不会锁定或同步该对象,因此如果它是线程安全的,则取决于实现。如果在该副本中原始对象的某些属性发生了更改,那么您可能会陷入一种奇怪的状态。如果您在多个线程中使用该克隆对象 - 您仍然遇到问题

对于您的特定示例,我认为代码很好。您要克隆的sdf对象可能永远不会改变,您不需要锁定或其他东西(似乎)。您只需为每个线程创建一个新的SimpleDateFormat对象,以确保线程安全 - 或者至少是这个想法 - 并且您可以通过使用clone来实现。

无论如何,如果你发现遗留代码中存在问题并且你不喜欢它,那么花一些时间和重构它总是比保持它更好,即使你不喜欢它。从长远来看,它几乎总能得到回报,因为它拥有更好,更易维护的代码,并且不会让下一代开发人员徘徊。例如,如果已升级到java 8,则可以使用线程安全的DateTimeFormatter,也可以使用某些外部库。或者至少在每次需要时创建一个新的SimpleDateFormat(SOME_CONSTANT_FORMAT)而不是依赖于对象的克隆 - 因为如果你只共享一个字符串常量(实际格式),那么它是不可变的和线程安全的。

答案 2 :(得分:1)

这不是答案。但是你可能仍然觉得有些信息很有趣。我做了几个实验。

首先,我有两个线程使用相同的SimpleDateFormat实例格式化日期。经过几次迭代后,他们开始给出不正确的结果,经过几百次迭代后,其中一个线程崩溃了。因此线程不安全似乎非常真实。

接下来,我使用原始SimpleDateFormat创建了一个线程格式日期,另一个使用克隆进行克隆并使用克隆进行格式化。两个线程现在都运行了几分钟,但仍然都能产生正确的结果。

这绝不保证这是您将始终看到的行为。文档非常清楚:SimpleDateFormat不是线程安全的,并且必须同步来自多个线程的所有访问。因此,使用这些信息需要您自担风险。

编辑:检查源代码似乎表明克隆操作按某种顺序复制字段,但不会修改原始字段。如果原始文件在另一个线程中执行任何工作,则可能导致克隆在创建后处于不一致状态,这反过来可能会或可能不会影响其正确工作。如果原始版仅用于克隆,我认为当前实现没有风险。正如您所说,实现可能会在以后的Java版本中进行更改,但我认为风险很小,并且引入线程不安全行为的风险更小。所有这些都是纯粹的猜测!