SimpleDateFormat经常被声明为static,在并发环境下公用一个实例,由于它不是线程安全的会产生两种问题:1.日期并不是预想的日期,乱七八糟的任何日期都可能出现。 2. 一定几率下抛异常。
所以有人就每次调用格式化都重新new一个SimpleDateFormat实例,这种方式会存在性能问题,因为其实例化是“expensive”的。所以最优的解决方案是采用ThreadLocal方式。代码如下:
<pre name="code" class="java">/**
* TheadLocal SimpleDateFormat
* Fix SimpleDateFormat concurrency problems and increase performance
*
* Date date = ConcurrentDateFormat.getInstance("yyyy-MM-dd HH:mm:ss").parse("2015-10-14 15:29:00");
* String dateStr = ConcurrentDateFormat.getInstance("yyyy-MM-dd").format(new Date());
*
* @author chouzi
*/
public class ConcurrentDateFormat {
private SimpleDateFormat dateFormat;
private Locale locale;
private static ThreadLocal<Map<key concurrentdateformat="">> dateFormatThreadLocal = new ThreadLocal<Map<key concurrentdateformat="">>();
private ConcurrentDateFormat(SimpleDateFormat df, Locale locale) {
this.dateFormat = df;
this.locale = locale;
}
public String format(Date date) {
return dateFormat.format(date);
}
public String format(Object date) {
return dateFormat.format(date);
}
/**
* Returns a <code>String</code> object which formated by
* the specified number of milliseconds since the
* standard base time known as "the epoch", namely January 1,
* 1970, 00:00:00 GMT.
*
* @param date the milliseconds since January 1, 1970, 00:00:00 GMT.
*/
public String format(long date) {
return dateFormat.format(new Date(date));
}
public Date parse(String date) throws ParseException {
return dateFormat.parse(date);
}
/**
* Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
* represented by this <tt>String</tt> object.
*
* @return the number of milliseconds since January 1, 1970, 00:00:00 GMT
* represented by this string date.
*/
public long parseToLong(String source) throws ParseException {
return parse(source).getTime();
}
public static ConcurrentDateFormat getInstance(String pattern) {
return getInstance(pattern, TimeZone.getDefault(), Locale.getDefault());
}
public static ConcurrentDateFormat getInstance(String pattern, TimeZone timeZone, Locale locale) {
if (pattern == null || locale == null) {
throw new NullPointerException("Parameter pattern or locale must be not null!");
}
Map<key concurrentdateformat=""> formatMap = dateFormatThreadLocal.get();
if (formatMap == null) {
formatMap = new HashMap<key concurrentdateformat="">();
dateFormatThreadLocal.set(formatMap);
}
Key key = new Key(pattern, locale);
ConcurrentDateFormat concurrentDateFormat = formatMap.get(key);
if (concurrentDateFormat == null) {
concurrentDateFormat = new ConcurrentDateFormat(new SimpleDateFormat(pattern, locale), locale);
formatMap.put(key, concurrentDateFormat);
}
concurrentDateFormat.setTimeZone(timeZone);
return concurrentDateFormat;
}
public void setTimeZone(TimeZone timeZone) {
this.dateFormat.setTimeZone(timeZone);
}
public TimeZone getTimeZone() {
return this.dateFormat.getTimeZone();
}
public Locale getLocale() {
return this.locale;
}
/**
* The key for ConcurrentDateFormat
*
* @author taugast
*/
private static class Key {
String pattern;
Locale locale;
Key(String pattern, Locale locale) {
this.pattern = pattern;
this.locale = locale;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((locale == null) ? 0 : locale.hashCode());
result = prime * result + ((pattern == null) ? 0 : pattern.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
Key other = (Key) obj;
if (locale == null) {
if (other.locale != null) {
return false;
}
} else if (!locale.equals(other.locale)) {
return false;
}
if (pattern == null) {
if (other.pattern != null) {
return false;
}
} else if (!pattern.equals(other.pattern)) {
return false;
}
return true;
}
}
public static final String YYYY_MM_DD_CN = "yyyy年MM月dd日";
public static final String MM_DD_CN = "MM月dd日";
public static final String YYYY = "yyyy";
public static final String YYYY_MM_DD_HYPHEN = "yyyy-MM-dd";
public static final String YYYY_MM_DD_SLASH = "yyyy/MM/dd";
public static final String HH_MM = "HH:mm";
public static final String DEFAULT_FORMAT = "yyyy-MM-dd HH:mm:ss";
}