题 Java:NumberFormat的同步问题?


我只使用java.text.NumberFormat将数字转换为更易读的字符串,逗号分隔数千个等等。基本上我将其定义为:

public static NumberFormat nf = NumberFormat.getInstance(Locale.US);

...然后我在任何线程中调用nf.format(some_number),我想在其中创建一个数字的可读版本。但是看看JavaDoc,它说:“数字格式通常不同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问一个格式,它必须在外部同步。”

如果我只使用NumberFormat对象的格式(数字)方法,是否会出现同步问题?我尝试使用NumberFormat.getInstance(Locale.US).format(number),但是每次我觉得可能并不真的需要这样做时会产生开销。这真的需要外部同步吗?或者,如果没有NumberFormat,有没有更简单,有效的方法来完成同样的事情?

谢谢!


15
2017-08-16 20:31


起源




答案:


即使格式是您调用的唯一方法,它仍然不是线程安全的。事实上,由于这个原因,我们在工作中遇到了错误。我们通常会动态创建NumberFormat对象,或者使用Gerco建议的ThreadLocal。如果你想得到想象,你可以继承NumberFormat并在format方法中,在委托NumberFormat上调用format之前进行同步,或者使用ThreadLocal来检索委托。

但是,我认为最简单的方法,特别是如果你要连续格式化/解析几个数字,就是手动使用ThreadLocal。


17
2017-08-16 20:39



用这个来衡量 gist.github.com/jontejj/5430320,结果发表于 1.microbenchmarks.appspot.com/run/jonatan.jonsson@softhouse.se/... 如果格式化数字是唯一发生的事情,那么增益实际上相当大。即使NumberFormat为创建的格式化程序保留了一个缓存,并且只在返回它们之前克隆它们。 - jontejj
这同样适用于所有子类..尤其是DateFormat - Snicolas


使用ThreadLocal <NumberFormat>。这样每个线程都将拥有它自己的私有NumberFormat实例,并且不需要同步,只需要最小的开销。


9
2017-08-16 20:33





看一下源代码 NumberFormat 和 DecimalFormat,似乎没有任何字段用于中间结果 - 唯一的问题是格式本身(例如小数位数)可以通过setter变化,所以一个线程可以改变它而另一个线程 format() 电话正在处理中,这当然会导致一团糟。

如果你从不使用setter,那么它 应该 没关系 - 但当然这只是当前的实施。取决于与API文档相反的情况,我感到不舒服。用一个 ThreadLocal 听起来是一个很好的妥协。


3
2017-08-16 20:47



我知道这是一个古老的答案,但是......你真的不应该使用源代码。即使您知道您正在使用的Java版本,供应商之间也会有所不同。 (例如,我在IBM和Sun / Oracle JVM之间看到了很多差异。) - Adam Crume
在我的JDK版本中,实际存储了中间结果,但正如您所说的YMMV。 - jontejj


NumberFormat 是一个 abstract class。调用时默认为 getInstance 是返回一个实例 DecimalFormatDecimalFormat 使用一堆字段来保持其在格式化过程中的位置,前缀和后缀的模式, booleans表示是否使用指数表示法和数千个分组, int用来描述整数和分数部分的大小等。

ThreadLocal 如果您期望任何并发格式化,选项是一个很好的方法。注意,所有的子类 abstract  Format class被认为不是线程安全的,因此格式化日期也应该非常谨慎地处理。


2
2017-08-16 21:11





没有理由共享NumberFormat对象。是的,它可能有同步问题(查看您的语言环境的源代码,您将看到它们使用成员变量,甚至格式化)。在您遇到性能问题(您很可能不会)之前,只需为每次使用创建一个新问题。

编辑 正如Michael Borgwardt指出的那样,我对成员变量的预感并不正确。仍然,为什么担心?使用LocalThread,克隆NumberFormat,或者只创建一个新的。对象创建方面的效率并不是真正的问题 大部分时间(但并非总是如此)


1
2017-08-16 20:42