日期格式化在高并发场景下的解决方案

一、SimpleDateFormat存在的线程安全问题

SimpleDateFormat线程不安全,是因为类中的calendar本身线程不安全

二、解决方案

1. 局部变量方式(不推荐)

每次格式化时间时都创建SimpleDateFormat对象。这方案虽然解决了线程安全问题,但会导致创建了大量SimpleDateFormat对象,会浪费内存和性能,所以不推荐在高并发场景使用。

代码示例:

public class DateUtil {
    public static String format(Date date, String format) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        return simpleDateFormat.format(date);
    }
}

2. ThreadLocal方式

通过ThreadLocal保存各个线程的SimpleDateFormat对象的副本,使得每个线程都使用自身的SimpleDateFormat对象,而达到互不干扰,同时执行性能也比较高,推荐使用。

代码示例:

public class DateUtil {
    /**
     * 常规日期时间格式,24小时制yyyy-MM-dd HH:mm:ss
     */
    public static final String NORMAL_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /**
     * 常规日期,yyyy-MM-dd
     **/
    public static final String NORMAL_DATE_FORMAT = "yyyy-MM-dd";
    /**
     * 锁对象
     */
    private static final Object LOCK_OBJ = new Object();
    /**
     * 存放不同的日期模板格式的SimpleDateFormat的Map
     */
    private static Map<String, ThreadLocal<SimpleDateFormat>> threadLocalMap = new HashMap<>(8);
    /**
     * 返回一个ThreadLocal的SimpleDateFormat,每个线程只会new一次
     *
     * @param pattern
     * @return
     */
    private static SimpleDateFormat getDateFormat(final String pattern) {
        ThreadLocal<SimpleDateFormat> tl = threadLocalMap.get(pattern);
        // 双重判断和同步,防止threadLocalMap这个单例被多次重复的SimpleDateFormat
        if (tl == null) {
            synchronized (LOCK_OBJ) {
                tl = threadLocalMap.get(pattern);
                if (tl == null) {
                    tl = ThreadLocal.withInitial(() -> new SimpleDateFormat(pattern));
                    threadLocalMap.put(pattern, tl);
                }
            }
        }
        return tl.get();
    }
    /**
     * 日期转字符串
     * 
     * @param date
     * @param format
     * @return
     */
    public static String format(Date date, String format) {
        return getDateFormat(format).format(date);
    }
    /**
     * 字符串转日期
     * 
     * @param dateStr
     * @param format
     * @return
     * @throws ParseException
     */
    public static Date parse(String dateStr, String format) throws ParseException {
        return getDateFormat(format).parse(dateStr);
    }
}

3. DateTimeFormatter方式

DateTimeFormatter是Java 8提供的专门处理日期和时间的类,线程安全,同时性能也不错。如果项目广泛使用Date和Calendar,是需要转换后使用的,所以自行考虑选择使用。

代码示例:

public class DateUtil {
    public static final DateTimeFormatter NORMAL_DATETIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    public static String format(LocalDate localDate, DateTimeFormatter formatter) {
        return formatter.format(localDate);
    }
    public static void main(String[] args) {
        System.out.println(DateUtil.format(LocalDate.now(), NORMAL_DATETIME_FORMAT));
    }
}

4. joda-time方式

一个第三方日期时间处理的类库,线程安全,经过高并发场景考验,推荐使用。

引入依赖:

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.10.10</version>
</dependency>

代码示例:

DateTimeFormatter dtfd = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dt6 = DateTime.parse("2022-11-20 7:20:45", dtf);

个人比较推荐使用ThreadLocal方式或joda-time方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值