使用Apache的FastDateFormat代替JDK的SimpleDateFormat,提升性能。

SimpleDateFormat的问题

在Java中,格式化日期通常使用SimpleDateFormat这个类。

我们知道,SimpleDateFormat是线程不安全的,主要原因是format方法内部调用calendar.setTime方法,整个过程都是没有加锁或同步的,如果同时有多个线程调用到这一步,则会出现线程安全问题。

public final String format(Date date) {
    return format(date, new StringBuffer(), DontCareFieldPosition.INSTANCE).toString();
}

// 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);
    ...
}

所以,大部分时候则是在方法内部new出新的DateFormat对象再做格式化,如:DateFormat df = new SimpleDateFormat("yyyy-MM-dd");

但在高访问量的情况下,频繁创建实例也会导致内存开销大和GC频繁问题。

FastDateFormat

Apache的commons-lang包下有个FastDateFormat可以方便的解决上述问题。

查看其源码:

// 2.6版本源码
public static synchronized FastDateFormat getInstance(String pattern, TimeZone timeZone, Locale locale) {
    FastDateFormat emptyFormat = new FastDateFormat(pattern, timeZone, locale);
    FastDateFormat format = (FastDateFormat) cInstanceCache.get(emptyFormat);
    if (format == null) {
        format = emptyFormat;
        format.init();  // convert shell format into usable one
        cInstanceCache.put(format, format);  // this is OK!
    }
    return format;
}

FastDateFormat通过getInstance静态方法获取对象,内部加了一个cInstanceCache缓存。当使用同样的pattern格式化时会命中缓存,返回缓存中的对象,避免重复创建实例。

拿到对象调用format方法,可以看出Calender是在format方法中创建的,所以不会出现setTime的线程安全问题。

public String format(Date date) {
    Calendar c = new GregorianCalendar(mTimeZone, mLocale);
    c.setTime(date);
    return applyRules(c, new StringBuffer(mMaxLengthEstimate)).toString();
}

而且getInstance这个方法是加锁的,以保证获取实例的线程安全性。在新的版本(3.0以上)中getInstance方法内更加使用ConcurrentMap做缓存提高并发性能。

// 3.0版本源码
abstract class FormatCache<F extends Format> {
    static final int NONE = -1;
    private final ConcurrentMap<MultipartKey, F> cInstanceCache;
    private final ConcurrentMap<MultipartKey, String> cDateTimeInstanceCache;
    ...
}

private static final FormatCache<FastDateFormat> cache = new FormatCache() {
    protected FastDateFormat createInstance(String pattern, TimeZone timeZone, Locale locale) {
        return new FastDateFormat(pattern, timeZone, locale);
    }
};

public static FastDateFormat getInstance(String pattern, TimeZone timeZone, Locale locale) {
    return (FastDateFormat)cache.getInstance(pattern, timeZone, locale);
}

DateFormatUtils

基于上面FastDateFormat类,commons-lang包还提供了DateFormatUtils工具类,提供了两方面的功能:

  1. 提供静态的format方法,实现一行代码格式化日期。
  2. 内部初始化了一些final的FastDateFormat,方便开发者直接调用。

你可能感兴趣的:(Java开发)