如果我在工厂类中实现ThreadLocal会发生什么
我试图在Java中为日期和数字实现格式化程序。但是一些java中的格式化程序不是线程安全的,例如。 (首先,我不明白他们为什么不是像DateTimeFormat
那样的线程安全!)所以,在搜索了一下之后,我发现了ThreadLocal
变量。如果我在工厂类中实现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)
这还算是线程安全的我这个用法?
UPDATE:
答案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);
}
现在你回到每getMoneyFormatter
被调用时,一个新的ThreadLocal
。您只应初始化一次。
但是,使用ThreadLocal
可能会导致资源泄漏,所以除非您确实知道需要它,否则在需要时创建新的格式化程序会更简单。
它将是线程安全的,因为在getMoneyFormatter方法中,您将在每次调用getMoneyFormatter方法时创建ThreadLocal的特定实例。
但是这不是ThreadLocal的正确用法。您应该只初始化一次,它应该用于您想要存储特定于某个线程的变量的位置,然后同一线程稍后可以从ThreadLocal中获取该值。 ThreadLocal中的存储值仅对该线程可见,其他线程无法修改或更改。
在你的场景中,如果DecimalFormat是方法getMoneyFormatter的本地对象,那么它也将是线程安全的,在这种情况下你不需要ThreadLocal.Please检查下面的例子。
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);
}
当前我正在使用该项目中的几个线程。所以DecimalFormat必须使用ThreadLocal进行线程安全。 – 2015-01-21 14:32:32
方法本地对象始终是线程安全的... – ssood9 2015-01-21 16:08:35
卡亚曼说:“......资源泄漏”。如果某个线程t创建一个DecimalFormat实例(或任何其他对象)并将其放在一个ThreadLocal对象中,那么即使在线程t死后,ThreadLocal对象也将继续保存该引用。线程t创建的对象永远不会被垃圾收集。当你拥有一组固定的线程时,ThreadLocal很好用,但它可能会导致在不断创建新的短暂线程的应用程序中出现问题。 (注意:当请求队列中的积压更改时,某些ExecutorService实现会创建并销毁线程。) – 2015-01-21 17:55:12