SimpleDateFormat并发问题解决方案

时间:2022-09-12 11:20:40
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 yuyong.zhao */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 yuyong.zhao     */    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";}