我只使用java.text.NumberFormat将数字转换为更易读的字符串,逗号分隔数千个等等。基本上我将其定义为:
public static NumberFormat nf = NumberFormat.getInstance(Locale.US);
...然后我在任何想要制作数字可读版本的线程中调用nf.format(some_number)。但是看看JavaDoc,它说:“数字格式通常不同步。建议为每个线程创建单独的格式实例。如果多个线程同时访问一个格式,它必须在外部同步。”
如果我只使用NumberFormat对象的格式(数字)方法,是否会出现同步问题?我尝试使用NumberFormat.getInstance(Locale.US).format(number),但是每次我觉得可能不需要时,都会产生相关的开销。这真的需要外部同步吗?或者,如果没有NumberFormat,是否有更简单有效的方法来完成同样的事情?
谢谢!
答案 0 :(得分:19)
即使格式是您调用的唯一方法,它仍然不是线程安全的。事实上,由于这个原因,我们在工作中遇到了错误。我们通常会动态创建NumberFormat对象,或者使用Gerco建议的ThreadLocal。如果你想得到想象,你可以继承NumberFormat并在format方法中,在委托NumberFormat上调用format之前进行同步,或者使用ThreadLocal来检索委托。
但是,我认为最简单的方法,特别是如果你要连续格式化/解析几个数字,就是手动使用ThreadLocal。
答案 1 :(得分:10)
使用ThreadLocal< NumberFormat>。这样每个线程都有自己的私有NumberFormat实例,不需要同步,只需要很少的开销。
答案 2 :(得分:3)
查看NumberFormat
和DecimalFormat
的源代码,似乎没有任何字段用于中间结果 - 唯一的问题是格式本身(例如小数位数)通过setter是可变的,因此一个线程可以在处理另一个format()
调用时更改它,这当然会导致混乱。
如果你从不使用setter,那么它应该可以 - 但当然这只是当前的实现。取决于与API文档相反的情况,我感到不舒服。使用ThreadLocal
听起来是一个很好的妥协。
答案 3 :(得分:2)
NumberFormat
是abstract class
。调用getInstance
时的默认设置是返回DecimalFormat
的实例。 DecimalFormat
使用一堆字段来维护其在格式化过程中的位置,前缀和后缀的模式,boolean
指示是否使用指数表示法和数千个分组,int
s描述其整数和分数部分的大小等。
如果您期望任何并发格式化,ThreadLocal
选项是一个很好的方法。请注意,abstract
Format
类的所有子类都被认为不是线程安全的,因此格式化日期也应该非常谨慎地处理。
答案 4 :(得分:1)
没有理由共享NumberFormat对象。是的,它可能有同步问题(查看您的语言环境的源代码,您将看到它们使用成员变量,甚至格式化)。在您遇到性能问题(您很可能不会)之前,只需为每次使用创建一个新的问题。
编辑正如Michael Borgwardt指出的那样,我对成员变量的预感并不正确。还是,为什么担心?使用LocalThread,克隆NumberFormat,或者只创建一个新的。在对象创建方面的效率并非真正关注大部分时间(但并非总是如此)。