如果我在工厂类中实现ThreadLocal会发生什么

时间:2015-01-21 07:26:09

标签: java multithreading static factory thread-local

我试图在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);
  }

2 个答案:

答案 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);
    }