Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如果在工厂类中实现ThreadLocal,会发生什么_Java_Multithreading_Static_Factory_Thread Local - Fatal编程技术网

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

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

我正在尝试用Java实现日期和数字的格式化程序。但是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实现会在请求队列中的积压更改时创建和销毁线程。)