2010-12-13 72 views
4

我在JSF 1.2中创建了一个自定义Converter来转换Date对象。日期有一个非常特殊的格式。我已经使用核心Java SimpleDateFormat类实现了我的转换器,使用下面我的代码注释中显示的格式化字符串来完成转换。这一切工作正常。JSF自定义转换为日期 - 是线程安全吗?

我的问题是关于线程安全。 SimpleDateFormat API文档声明它不是线程安全的。出于这个原因,我为我的转换器对象的每个实例创建了日期格式对象的单独实例。但是,我不确定这是否足够。我的DateFormat对象存储为DTGDateConverter的成员。

问题:两个线程每个都会同时访问JSF中Converter对象的同一个实例吗?

如果答案是肯定的,那么我的转换器可能有风险。

/** 
* <p>JSF Converter used to convert from java.util.Date to a string. 
* The SimpleDateFormat format used is: ddHHmm'Z'MMMyy.</p> 
* 
* <p>Example: October 31st 2010 at 23:59 formats to 312359ZOCT10</p> 
* 
* @author JTOUGH 
*/ 
public class DTGDateConverter implements Converter { 

    private static final Logger logger = 
     LoggerFactory.getLogger(DTGDateConverter.class); 

    private static final String EMPTY_STRING = ""; 

    private static final DateFormat DTG_DATE_FORMAT = 
     MyFormatterUtilities.createDTGInstance(); 

    // The 'format' family of core Java classes are NOT thread-safe. 
    // Each instance of this class needs its own DateFormat object or 
    // runs the risk of two request threads accessing it at the same time. 
    private final DateFormat df = (DateFormat)DTG_DATE_FORMAT.clone(); 

    @Override 
    public Object getAsObject(
      FacesContext context, 
      UIComponent component, 
      String stringValue) 
      throws ConverterException { 
     Date date = null; 
     // Prevent ParseException when an empty form field is submitted 
     // for conversion 
     if (stringValue == null || stringValue.equals(EMPTY_STRING)) { 
      date = null; 
     } else { 
      try { 
       date = df.parse(stringValue); 
      } catch (ParseException e) { 
       if (logger.isDebugEnabled()) { 
        logger.debug("Unable to convert string to Date object", e); 
       } 
       date = null; 
      } 
     } 
     return date; 
    } 

    @Override 
    public String getAsString(
      FacesContext context, 
      UIComponent component, 
      Object objectValue) 
      throws ConverterException { 
     if (objectValue == null) { 
      return null; 
     } else if (!(objectValue instanceof Date)) { 
      throw new IllegalArgumentException(
       "objectValue is not a Date object"); 
     } else { 
      // Use 'toUpperCase()' to fix mixed case string returned 
      // from 'MMM' portion of date format 
      return df.format(objectValue).toUpperCase(); 
     } 
    } 

} 
+0

balusC?!你在哪里 – mkoryak 2010-12-13 15:48:56

+2

@mkoryak:我有一份工作和生活,以及:) – BalusC 2010-12-13 16:30:45

+0

111K代表,这就是所有即时通讯要说:) – mkoryak 2010-12-13 16:56:28

回答

7

将在两个线程同时每次访问JSF一个转换器对象相同的实例?

取决于您如何使用转换器。如果您使用

<h:inputWhatever> 
    <f:converter converterId="converterId" /> 
</h:inputWhatever> 

然后一个新的实例将在视图中的每个输入元素,这是线程安全的(预计终端用户在同一会话两个浏览器标签两个相同的观点非常罕见的边缘情况来创建同时对这两个视图发布回传)。

不过,若你使用

<h:inputWhatever converter="#{applicationBean.converter}" /> 

那么同样的实例将在整个应用程序,这是因此不是线程安全的所有视图共享。

但是,您每次创建转换器时都要克隆一个静态的DataFormat实例。这部分已经不是线程安全的。您可能冒着克隆实例的同时更改其内部状态的风险,因为它已在其他地方使用。另外,克隆现有实例并不一定比创建新实例便宜。

无论你如何使用转换器,我都会建议只声明threadlocal(即在方法块内)。如果每次创建DateFormat的代价是一个主要问题(您是否对其进行了描述?),那么考虑将其替换为JodaTime

+0

我已经在我的faces-config.xml中将它声明为,并且在第一种情况下通过converter-id引用它。我没有分析它,所以你在那里写得很好。谢谢! – 2010-12-13 16:47:03

2

日期格式不同步。建议使用 为每个线程创建单独的 格式实例。如果多个线程同时访问格式 ,则必须在外部同步 。

是的,它在这里不是线程安全的。

把它本地的方法和每个线程创建实例

+0

所以你是说JSF Converter实例是由多个线程同时访问? – 2010-12-13 16:11:50

+0

@Jim虽然这是第二点,但首先你要克隆静态DateFormat对象,该对象将在任何请求下复制对象的状态,但在任何情况下都不可取。 – 2010-12-13 16:23:56

+0

我想要做的是尽量减少创建的DateFormat对象的数量,或找到一种安全高效的方式来共享或重用它们。我只能想象,每次从格式字符串创建这些代码都很昂贵。它们在我的JSF应用程序(可能有大量条目)中的数据表内部使用,因此创建所有这些DateFormat对象的开销可能并非微不足道。 – 2010-12-13 16:30:33