【问题标题】:What will happen if I implement ThreadLocal in a factory class如果我在工厂类中实现 ThreadLocal 会发生什么
【发布时间】:2015-03-19 15:29:56
【问题描述】:

我正在尝试在 Java 中实现日期和数字的格式化程序。但是java中的一些格式化程序不是线程安全的,例如。 DecimalFormat, SimpleDateFormat!(首先,我不明白为什么它们不像DateTimeFormat!)所以,经过一番搜索,我遇到了ThreadLocal 变量。

我见过的所有关于ThreadLocal 的sn-ps,他们都使用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的答案解决了我的问题。

我的sn-p如下:

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

【问题讨论】:

  • Kayaman 说,“……资源泄露。”如果某个线程 t 创建了一个 DecimalFormat 实例(或任何其他对象)并将其粘贴在 ThreadLocal 对象中,则即使在线程 t 死亡后,ThreadLocal 对象仍将继续持有该引用。线程 t 创建的对象永远不会被垃圾回收。当您有一组固定的线程时,ThreadLocal 很适合使用,但它可能会在不断创建新的短期线程的应用程序中导致麻烦。 (注意:一些 ExecutorService 实现会随着请求队列中积压的变化而创建和销毁线程。)

标签: java multithreading static factory thread-local


【解决方案1】:

这将是线程安全的,因为在 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);
    }

【讨论】:

  • 这将是我目前正在工作的项目中的几个线程。因此必须使用 ThreadLocal 使 DecimalFormat 成为线程安全的。
【解决方案2】:

现在,每次调用 getMoneyFormatter 时,您都会返回一个新的 ThreadLocal。你应该只初始化一次。

但是,使用 ThreadLocal 可能会导致资源泄漏,因此除非您真的知道自己会需要它,否则在需要时创建一个新的格式化程序会更简单。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-06
    • 1970-01-01
    • 2017-12-28
    • 2016-02-13
    • 2014-03-01
    • 1970-01-01
    相关资源
    最近更新 更多