Java 如果在工厂类中实现ThreadLocal,会发生什么
我正在尝试用Java实现日期和数字的格式化程序。但是java中的一些格式化程序不是线程安全的,例如,Java 如果在工厂类中实现ThreadLocal,会发生什么,java,multithreading,static,factory,thread-local,Java,Multithreading,Static,Factory,Thread Local,我正在尝试用Java实现日期和数字的格式化程序。但是java中的一些格式化程序不是线程安全的,例如,DecimalFormat、SimpleDateFormat!(首先,我不明白它们为什么不像DateTimeFormat!)那样是线程安全的。因此,在搜索了一点之后,我发现了ThreadLocal变量 我见过的所有关于ThreadLocal的代码片段都使用final。当然,有一个formatter实例是有意义的。但是,假设我们需要一个格式化程序,但需要3个模式 FormatFactory.java
DecimalFormat、SimpleDateFormat
!(首先,我不明白它们为什么不像DateTimeFormat
!)那样是线程安全的。因此,在搜索了一点之后,我发现了ThreadLocal
变量
我见过的所有关于ThreadLocal
的代码片段都使用final
。当然,有一个formatter实例是有意义的。但是,假设我们需要一个格式化程序,但需要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;
}
};
}
}
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)
public static String money(BigDecimal amount, String pattern) {
return FormatFactory.getMoneyFormatter(pattern).format(amount);
}
在这种情况下它仍然是线程安全的吗
更新:
答案解决了我的问题
我的代码片段如下所示:
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();
}
现在,每次调用
getMoneyFormatter
时,您都会返回一个新的ThreadLocal
。您应该只初始化它一次
但是,使用
ThreadLocal
可能会导致资源泄漏,因此,除非您确实知道自己需要它,否则在需要时只创建一个新的格式化程序会更简单。它将是线程安全的,因为在getMoneyFormatter方法中,您在每次调用getMoneyFormatter方法时都会创建ThreadLocal的Sperate实例
但这不是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);
}
这将是我目前正在工作的项目中的几个线程。因此,必须使用ThreadLocal使DecimalFormat具有线程安全性。Kayaman说,“…资源泄漏”。如果某个线程t创建DecimalFormat实例(或任何其他对象)并将其粘贴到ThreadLocal对象中,ThreadLocal对象将继续保留引用,即使线程t死亡。线程t创建的对象永远不会被垃圾收集。当您有一组固定的线程时,可以使用ThreadLocal,但它可能会导致在不断创建新的短期线程的应用程序中出现问题。(注意:一些ExecutorService实现会在请求队列中的积压更改时创建和销毁线程。)