SimpleDateFormat은 JDK 전 벤더사에 걸쳐서 Thread Safe하지 않으며 객체생성시(new SimpleDateFormat) 많은 연산으로 CPU를 잡아먹는다.
Java Doc 내용:
Synchronization
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.
그래서 SimpleDateFormat을 여러 Thread에서 사용시에 엉뚱한 날짜가 계산되어 나오는 경우가 있다.
실제 문제가 발생하여 테스트시 format() 메소드만 타면 간혹 엉뚱한 값이 나왔다.
물론 매번 new SimpleDateFormat을 하면 괜찮지만 CPU를 상당히 많이 잡아 먹기 때문에 좋지 않다.
개인적으로 테스트 해보기론 1, 2번을 사용할 경우 어느 정도 성능이 약간 떨어지긴하지만 정상적인 결과를 가져온다. 3번의 경우 속도가 빠르며 어느정도의 부하상황(여러 Thread들이 사용하는 경우)을 견디긴 하지만 많은 Thread에서 생성하여 테스트할 경우 엉뚱한 값이 나옴을 볼 수 있었다. 어느 정도의 방어로직은 되지만 100% 보장되는 방법은 아니다.
결국 결론은 apache의 FastDateFormat으로 가는 것 같다.
가능한 방법을 모두 정리해둔 주소를 googling으로 찾을 수 있었다. (http://solidsimplesafe.com/view/13)
위 주소의 마지막 내용을 보면 속도 및 안정성에 대해 간략한 표가 정리되어있다.
발췌 :
Style
speed ranking
notes
FastDateFormat
1
safe and fast
DateFormat - shared instance
2
unreliable, don't use
Joda
3
likely to be in java 7, the way
DateFormat with synchronization
4
works, but brittle, could be easily broken by unwitting programmers
DateFromat with ne
5
works, but brittle, could be easily broken by unwitting programmers
Apache의 FastDateFormat(commons-lang-2.4.jar) 를 보면 format() 메소드만 지원되고 parse()는 지원하지 않는다.
또한 new FastDateFormat을 사용하지 않고 FastDateFormat.getInstance()를 사용하면 Map을 통해 instance를 sharing 한다.
실제 FastDateFormat 소스를 보면 상당히 간결하며 commons-lang-2.4.jar에 있는 Validate Class(?)와 FastDateFormat Class만 있으도 된다.
다음은 SimpleDateFormat을 사용한 경우와 FastDateFormat을 사용한 경우에 대해 테스트한 샘플이다.
소스가 왜이리 더럽냐고 한다면 그저 마주볼뿐..
SimpleDateFormat을 사용하는 경우 샘플 소스 : 엉뚱한 값이 나올 경우 콘솔에 print 한다.
//비정상적인 값이 나온다면 print 한다.
if ( !formatTodayString.equals(orgTodayString) || !formatYesterdayString.equals(orgYesterdayString)) {
System.out.println("today : " + formatTodayString + " , yesterday : "+ formatYesterdayString);
}
}
}
public void setFormatString(String formatString) {
this.formatString = formatString;
}
public void setOrgTodayString(String orgTodayString) {
this.orgTodayString = orgTodayString;
}
public void setOrgYesterdayString(String orgYesterdayString) {
this.orgYesterdayString = orgYesterdayString;
}
public void setOrgTodayDate(Date orgTodayDate) {
this.orgTodayDate = orgTodayDate;
}
public void setOrgYesterdayDate(Date orgYesterdayDate) {
this.orgYesterdayDate = orgYesterdayDate;
}
}
/*
* SimpleDateFormat의 날짜 계산이 수행되는 Class
* 매번 new SimpleDateFormat에 따른 CPU 과점유를 막기위해 instance sharing을 사용.
*/
class SdfClass {
private static Map sdfMap = null;
public String getDate(String format, Date date){
return getInstance(format).format(date);
}