SimpleDateFormat 은 Thread-safe 하지 않다.

멀티스레드에서 동시에 사용하면 이상한 오류가 나거나 Date 객체가 올바르게 생성되지 않는다(시간이 잘못 나온다).

문제가 되었던 코드

public class CommonUtils {
 
    private static Logger logger = LoggerFactory.getLogger(CommonUtils.class);
 
    private static SimpleDateFormat ORIGIN_FORMAT_TYPE_1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.KOREAN);
    private static SimpleDateFormat ORIGIN_FORMAT_TYPE_2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.KOREAN);
    private static SimpleDateFormat TRANS_FORMAT = new SimpleDateFormat("yyyy.MM.dd. (EEE) hh:mm:ss", Locale.KOREAN);
    private static SimpleDateFormat TRANS_FORMAT_AM_PM = new SimpleDateFormat(" a", Locale.US);    // Locale.KOREAN 으로 하면 '오전/오후'로 표기되므로, 해당부분만 Locale.US로 설정하여 문자열을 조합한다.
    private static SimpleDateFormat TRANS_FORMAT_FOR_HOTTOPIC = new SimpleDateFormat("yyyy.MM.dd HH:mm", Locale.KOREAN);
  
  
    // ...
  
    switch (type) {
        case 1 :
            to = ORIGIN_FORMAT_TYPE_1.parse(dateString);   // 이 부분!!! ****
            break;
        case 2 :
            to = ORIGIN_FORMAT_TYPE_2.parse(dateString);   // 이 부분!!! ****
            break;
        default:
            throw new ParseException(dateString, type);
    }

예외로그

java.lang.NumberFormatException: For input string: ""
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
    at java.lang.Long.parseLong(Long.java:431)
    at java.lang.Long.parseLong(Long.java:468)
    at java.text.DigitList.getLong(DigitList.java:177)
    at java.text.DecimalFormat.parse(DecimalFormat.java:1298)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1936)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312)
    at java.text.DateFormat.parse(DateFormat.java:335)

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:1303)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1936)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312)
    at java.text.DateFormat.parse(DateFormat.java:335)

java.lang.NumberFormatException: For input string: ".30"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
    at java.lang.Long.parseLong(Long.java:410)
    at java.lang.Long.parseLong(Long.java:468)
    at java.text.DigitList.getLong(DigitList.java:177)
    at java.text.DecimalFormat.parse(DecimalFormat.java:1298)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312)
    at java.text.DateFormat.parse(DateFormat.java:335)

재현코드

// 문제가 되는 부분
final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 2; i++) {
    executorService.execute(new Runnable() {
        @Override
        public void run() {
            try {
                System.out.println(simpleDateFormat.parse("2017-03-22 16:00:00"));
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    });
}

(1) 예외가 발생하거나

(2) 잘못된 시간으로 나옴

해결방법

  1. SimpleDateFormat를 매번 생성하거나
  2. syncronized 먹여서 사용하던가
  3. 아파치 커먼스 라이브러리의 DateUtils 을 사용
  4. Joda time의 DateTimeFormatter를 사용하거나 Java8의 DateTimeFormatter를 사용