我试图在Java中实现日期和数字的格式化程序。但是java中的一些Formatters不是线程安全的,例如。 DecimalFormat, SimpleDateFormat
!(首先,我不明白为什么它们不像DateTimeFormat
那样是线程安全的!)所以,经过搜索后,我遇到了ThreadLocal
个变量。
我见过ThreadLocal
的所有摘录,他们使用final
。当然,有一个格式化程序实例是有意义的。但是,假设我们需要一个格式化器,但需要3种模式。
FormatFactory.java
public class FormatFactory {
public static ThreadLocal<DecimalFormat> getMoneyFormatter(final String pattern) {
return new ThreadLocal<DecimalFormat>() {
@Override
public DecimalFormat initialValue() {
DecimalFormat decFormat = new DecimalFormat(pattern);
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator(',');
if (!pattern.equals(FormatPatterns.MT940_DECIMAL)) {
symbols.setGroupingSeparator('.');
}
decFormat.setMinimumFractionDigits(2);
decFormat.setDecimalFormatSymbols(symbols);
return decFormat;
}
};
}
}
Format.java
public static String money(BigDecimal amount, String pattern) {
return FormatFactory.getMoneyFormatter(pattern).get().format(amount);
}
用法
Format.money(balance, FormatPatterns.MT940_DECIMAL)
Format.money(balance, FormatPatterns.SIGNED_MONEY)
Format.money(balance, FormatPatterns.MONEY)
在这种用法中它仍然是线程安全的吗?
更新
答案here解决了我的问题。
我的代码段如下:
private static final ConcurrentMap<String, ThreadLocal<DecimalFormat>> decimialFormatsByPattern = new ConcurrentHashMap<String, ThreadLocal<DecimalFormat>>();
public static DecimalFormat getMoneyFormatter(final String pattern) {
ThreadLocal<DecimalFormat> decimalFormatter = decimialFormatsByPattern.get(pattern);
if (decimalFormatter == null) {
decimalFormatter = new ThreadLocal<DecimalFormat>() {
@Override
public DecimalFormat initialValue() {
DecimalFormat decFormat = new DecimalFormat(pattern);
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator(',');
if (!pattern.equals(FormatPatterns.MT940_DECIMAL)) {
symbols.setGroupingSeparator('.');
}
decFormat.setMinimumFractionDigits(2);
decFormat.setDecimalFormatSymbols(symbols);
return decFormat;
}
};
decimialFormatsByPattern.putIfAbsent(pattern, decimalFormatter);
}
return decimalFormatter.get();
}
用法
public static String money(BigDecimal amount, String pattern) {
return FormatFactory.getMoneyFormatter(pattern).format(amount);
}
答案 0 :(得分:2)
现在,每次调用ThreadLocal
时,您都会返回新的getMoneyFormatter
。你应该只初始化一次。
但是,使用ThreadLocal
会导致资源泄漏,因此除非您确实知道自己需要资源,否则只需在需要时创建新的格式化程序就更简单了。
答案 1 :(得分:0)
它将是线程安全的,因为在getMoneyFormatter方法中,每次调用getMoneyFormatter方法时都会创建一个ThreadLocal的特殊实例。
但这不是ThreadLocal的正确使用。你应该只将它初始化一次,它应该用在你想要存储特定于某个线程的变量的地方,然后同一个线程可以在以后从ThreadLocal获取值。 ThreadLocal中的存储值仅对该线程可见,其他线程无法修改或更改它。
在你的场景中,如果DecimalFormat将是方法getMoneyFormatter的本地对象,那么它也将是线程安全的,在这种情况下你不需要ThreadLocal。请检查以下示例。
public DecimalFormat getMoneyFormatter(final String pattern) {
DecimalFormat decFormat = new DecimalFormat(pattern);
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator(',');
if (!pattern.equals(FormatPatterns.MT940_DECIMAL)) {
symbols.setGroupingSeparator('.');
}
decFormat.setMinimumFractionDigits(2);
decFormat.setDecimalFormatSymbols(symbols);
return decFormat
}
public static String money(BigDecimal amount, String pattern) {
return FormatFactory.getMoneyFormatter(pattern).format(amount);
}