SimpleDateFormat高并发情况下报错问题

项目中常常会用到日期格式化,一般是使用SimpleDateFormat,某天偶然听某同事谈到偶尔会有日期转换报错,于是研究了一下。

查看SimpleDateFormat源码,发现作者有一段注释如下:

原来,SimpleDateFormat并不是线程安全的,作者推荐为每一个线程创建一个单独的实例,或者为SimpleDateFormat加锁。

再看同事的代码,SimpleDateFormat是一个静态变量,没有加锁,且直接提供给N多方法调用,看来原因就在这里。

为什么SimpleDateFormat不是线程安全的呢?

通过源码可以知道SimpleDateFormat的format方法实际操作的就是Calendar。

因为我们声明SimpleDateFormat为static变量,那么它的Calendar变量也就是一个共享变量,可以被多个线程访问

假设线程A执行完calendar.setTime(date),把时间设置成2019-01-02,这时候被挂起,线程B获得CPU执行权。线程B也执行到了calendar.setTime(date),把时间设置为2019-01-03。线程挂起,线程A继续走,calendar还会被继续使用(subFormat方法),而这时calendar用的是线程B设置的值了,而这就是引发问题的根源,出现时间不对,线程挂死等等。

扫描二维码关注公众号,回复: 5535119 查看本文章

原来如此,那么如何解决呢?

解决方案

一、每次使用新创建对象

哪里用到哪里建,就没有啥线程安全问题了,但是增加GC负担,不推荐。

二、加锁

	private static SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	public static String formatDate(Date date) {
		synchronized (formater) {
			return formater.format(date);
		}
	}
	
	public static Date parseDate(String dateStr) throws ParseException {
		synchronized (formater) {
			return formater.parse(dateStr);
		}
	}

简单暴力,缺点是高并发下略微有点影响性能,阻塞线程。

三、ThreadLocal

	private static ThreadLocal<DateFormat> threadFormater = new ThreadLocal<DateFormat>() {
		@Override
		protected DateFormat initialValue() {
			return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		}
	};

	public static String tlFormatDate(Date date) {
		return threadFormater.get().format(date);
	}

	public static Date tlParseDate(String dateStr) throws ParseException {
		return threadFormater.get().parse(dateStr);
	}

综合一二的优缺点,解决了线程竞争问题,安全高效。

四、基于JDK1.8的DateTimeFormatter

	private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

	public static String jdk8FormatDate(LocalDateTime date) {
		return dateTimeFormatter.format(date);
	}

	public static LocalDateTime jdk8ParseDate(String dateStr) throws ParseException {
		return LocalDateTime.parse(dateStr, dateTimeFormatter);
	}

java.time.format.DateTimeFormatter是jdk新加入的日期格式化工具类,解决了SimpleDateFormat的线程安全问题,如果使用的是jdk8以上,强烈推荐使用。缺点嘛,要使用instant代替Date,LocalDateTime替代Calendar,对老代码来说有点难受。

猜你喜欢

转载自blog.csdn.net/kris1122/article/details/87966746