【Javaトラップ】SimpleDateFormat時間フォーマットマルチスレッド異常


SimpleDateFormatトラップ
SimpleDateFormatはjavaでよく使われる時間フォーマットツールで、時間フォーマットを変換するだけでなく、文字列を時間タイプに解析することもでき、使いやすいが、トラップはそこにある.時間文字列が完全に正しいのを見ても、正しくフォーマットできないか、時間タイプが間違っているのに、他のフォーマットに変換できないか、フォーマット変換エラーがランダムに発生している場合は、マルチスレッドが合併したトラップに陥っているかどうかを考えてみましょう.
呼び出し:
private static SimpleDateFormat sdf = new SimpleDataFormat("yyyy-MM-dd HH:mm:ss");
...
...

public static Date parseDateFromString(String str){
     try{
         return sdf.parse(str);
     }catch(Exception e){
         //TODO
     }
}
...
...

マルチスレッドの場合、データはランダムに例外を放出します.
java.lang.NumberFormatException: multiple points
        at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1082)
        at java.lang.Double.parseDouble(Double.java:510)
        at java.text.DigitList.getDouble(DigitList.java:151)
        at java.text.DecimalFormat.parse(DecimalFormat.java:1302)
        at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934)
        at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311)
        at java.text.DateFormat.parse(DateFormat.java:335)

この問題は、SimpleDateFormat自体がスレッドセキュリティではないため、シングルマシンの場合のデバッグでは問題を発見することが困難です.
SimpleDateFormat.java
 *** Date formats are 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.
 *
 * @see          "http://java.sun.com/docs/books/tutorial/i18n/format/simpleDateFormat.html">Java Tutorial
 * @see          java.util.Calendar
 * @see          java.util.TimeZone
 * @see          DateFormat
 * @see          DateFormatSymbols
 * @version      1.88, 05/28/08
 * @author       Mark Davis, Chen-Lieh Huang, Alan Liu
 */
public class SimpleDateFormat extends DateFormat {
**
..
protected Calendar calendar;
..
    // Called from Format after creating a FieldDelegate
        private StringBuffer format(Date date, StringBuffer toAppendTo,
                                    FieldDelegate delegate) {
            // Convert input date to time field list
            calendar.setTime(date);
    .....
    }
    public Date parse(String text, ParsePosition pos)
    {
        int start = pos.index;
        int oldStart = start;
    int textLength = text.length();
    calendar.clear(); // Clears all the time fields
    ...
    ...
    }
}

上のコードクリップから、マルチスレッドではオブジェクトcalendarが頻繁に変更され、予知できないエラーが発生することがわかります.
ソリューション:
  • SimpleDateFormatはメソッドの内部でインスタンス化され、変数を再定義するたびによくなくなり、formatオブジェクトが頻繁に作成され、パフォーマンスに影響を及ぼす
  • 同期コードブロックは、sdf共有変数に対してsynchronize(sdf)をロックするのはよくなく、ロックを発行するとシステムの同時性能が低下し、限界の場合は単一スレッドに劣化し、
  • を推奨しない.
  • はThreadLocalを使用し、分離スレッド共有変数は以下のように推奨される:
      private static ThreadLocal threadLocal = new ThreadLocal() {
      @Override
      protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };
    public static Date parse(String dateStr) throws ParseException {
        return threadLocal.get().parse(dateStr);
    }public static String format(Date date) {
        return threadLocal.get().format(date);
    }
  • の最後の部分