2015-01-21 176 views
2

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

卡亚曼说:“......资源泄漏”。如果某个线程t创建一个DecimalFormat实例(或任何其他对象)并将其放在一个ThreadLocal对象中,那么即使在线程t死后,ThreadLocal对象也将继续保存该引用。线程t创建的对象永远不会被垃圾收集。当你拥有一组固定的线程时,ThreadLocal很好用,但它可能会导致在不断创建新的短暂线程的应用程序中出现问题。 (注意:当请求队列中的积压更改时,某些ExecutorService实现会创建并销毁线程。) – 2015-01-21 17:55:12

回答

2

现在你回到每getMoneyFormatter被调用时,一个新的ThreadLocal。您只应初始化一次。

但是,使用ThreadLocal可能会导致资源泄漏,所以除非您确实知道需要它,否则在需要时创建新的格式化程序会更简单。

0

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

当前我正在使用该项目中的几个线程。所以DecimalFormat必须使用ThreadLocal进行线程安全。 – 2015-01-21 14:32:32

+0

方法本地对象始终是线程安全的... – ssood9 2015-01-21 16:08:35

相关问题