多线程下的时间格式化工具类--SimpleDateFormat

200 阅读1分钟

SimpleDateFormat并非线程安全,在实际开发中,代码复用的思想深深影响我,某次把老项目中的时间格式化工具类SimpleDateFormat抽成了静态常量,结果导致并发下时间异常。

  • 下面跟随源码看下问题出在哪里。

位置

  • SimpleDateFormat ==> parse() ==> CalendarBuilder.establish()
Calendar establish(Calendar cal) {
    boolean weekDate = isSet(WEEK_YEAR)
                        && field[WEEK_YEAR] > field[YEAR];
    if (weekDate && !cal.isWeekDateSupported()) {
        // Use YEAR instead
        if (!isSet(YEAR)) {
            set(YEAR, field[MAX_FIELD + WEEK_YEAR]);
        }
        weekDate = false;
    }
    //就是这一步;会把fields[] 和stamp 置空
    cal.clear();
    // Set the fields from the min stamp to the max stamp so that
    // the field resolution works in the Calendar.
    for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {
        for (int index = 0; index <= maxFieldIndex; index++) {
            if (field[index] == stamp) {
                cal.set(index, field[MAX_FIELD + index]);
                break;
            }
        }
    }

    if (weekDate) {
        int weekOfYear = isSet(WEEK_OF_YEAR) ? field[MAX_FIELD + WEEK_OF_YEAR] : 1;
        int dayOfWeek = isSet(DAY_OF_WEEK) ?
                            field[MAX_FIELD + DAY_OF_WEEK] : cal.getFirstDayOfWeek();
        if (!isValidDayOfWeek(dayOfWeek) && cal.isLenient()) {
            if (dayOfWeek >= 8) {
                dayOfWeek--;
                weekOfYear += dayOfWeek / 7;
                dayOfWeek = (dayOfWeek % 7) + 1;
            } else {
                while (dayOfWeek <= 0) {
                    dayOfWeek += 7;
                    weekOfYear--;
                }
            }
            dayOfWeek = toCalendarDayOfWeek(dayOfWeek);
        }
        cal.setWeekDate(field[MAX_FIELD + WEEK_YEAR], weekOfYear, dayOfWeek);
    }
    return cal;
}

解决办法

  • 单例使用,每次new
  • 使用Java8的新类 DateTimeFormatter,推荐工厂方法DateTimeFormatter.ofPattern("yyyy-MM-dd");