我在哪里线程安全?

时间:2012-01-18 13:43:35

标签: java multithreading thread-safety

我正在使用一个对象,它主要用作字符串映射的包装器:

public class Wrapper {
  private Map<String, String> values;
  private Formatter formatter;

  public BigDecimal getSpecialValue() {
    String result = values.get("Special");
    return formatter.formatNumber(result);
  }
}

上述格式化程序大致用作SimpleDateFormat

的映射器
 public class Formatter {
   private static final NumberFormat NUMBER_FORMAT;

   public BigDecimal formatNumber(String s) {
     Number num = NUMBER_FORMAT.parse(s);
     if (num instanceof Integer) {
       return new BigDecimal((Integer) num);
     } else if (num instanceof Double) {
       return new BigDecimal((Double) num);
     } ...
   }
 }

当我一次通过多个线程访问getSpecialValue()方法时,一些行为准确,只能通过并发访问来解释,例如可能有NumberFormatException或{{1}其中解析后的字符串是.430.430而不是.430,依此类推。

有两个方面引起我的兴趣: 1.)包装器只能以只读方式访问。虽然对该集合的访问不同步,但我认为这应该始终有效。 2.)在第一次发现问题的尝试之一中,我更改了ParseException类的构造函数以执行Wrapper方法(显然是单线程),这消除了执行时的所有异常。

有人可以解释一下吗?

编辑: formatNumber类中的映射填充在构造函数中,最明确的是单线程。然后设计包装器使得映射是不可变的。

2 个答案:

答案 0 :(得分:9)

只需在jdoc中搜索“thread”就可以找到NumberFormat类的以下内容:Number formats are generally not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

答案 1 :(得分:1)

  1. NumberFormat不是线程安全的。因此将其存储在静态变量中并从多个线程中使用它将导致问题。可以同步此对象的每次使用,也可以将其存储在ThreadLocal变量中。

  2. 即使只能以只读方式访问包装器,也必须有一段时间初始化和填充值映射,以及创建格式化程序的位置,否则就不会有任何格式化。您没有显示该类的真实完整代码,因此您在此类中也可能至少存在可见性问题。