使用 SimpleDateFormat?项目崩没老铁Exception in thread :java.lang.NumberFormatException: For input string: ""

SimpleDateFormat


日常开发中,我们经常需要使用时间相关类,说到时间相关类,想必大家对SimpleDateFormat并不陌生。主要是用它进行时间的格式化输出和解析,挺方便快捷的,但是SimpleDateFormat并不是一个线程安全的类。在多线程情况下,会出现异常,想必有经验的小伙伴也遇到过。下面我们就来分析分析SimpleDateFormat为什么不安全?是怎么引发的?以及多线程下有那些SimpleDateFormat的解决方案?


引言


先看看《阿里巴巴开发手册》对于SimpleDateFormat是怎么看待的:


在这里插入图片描述

附《阿里巴巴Java开发手册》v1.4.0(详尽版)下载链接:《阿里巴巴开发手册》v 1.4.0.pdf


问题场景复现

一般我们使用SimpleDateFormat的时候会把它定义为一个静态变量,避免频繁创建它的对象实例,如下代码:

import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.expression.ParseException;

public class SimpleDateFormatTest {

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

	public static String formatDate(Date date) throws ParseException {
		return sdf.format(date);
	}

	public static Date parse(String strDate) throws ParseException {
		return sdf.parse(strDate);
	}

	public static void main(String[] args) throws InterruptedException, ParseException {
		System.out.println(sdf.format(newDate()));
	}

}

是不是感觉没什么毛病?单线程下自然没毛病了,都是运用到多线程下就有大问题了。 测试下:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.text.ParseException;

public class SimpleDateFormatTest {

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

	public static String formatDate(Date date) throws ParseException {
		return sdf.format(date);
	}

	public static Date parse(String strDate) throws ParseException {
		return sdf.parse(strDate);
	}

	public static void main(String[] args) throws InterruptedException, ParseException {
		ExecutorService service =Executors.newFixedThreadPool(100);

		for(int i=0;i <20;i++){
	        service.execute(()->{
	            for(int j =0;j <10;j++){
	                try{
	                    System.out.println(parse("2019-05-13 11:30:59"));
	                }catch(ParseException e){
	                    e.printStackTrace();
	                }
	            }
	        });
	    }
	    
	// 等待上述的线程执行完
	service.shutdown();
	service.awaitTermination(1,TimeUnit.DAYS);
	}

}

控制台输出如下:(你看这不崩了老铁(有点飘哦)?部分线程获取的时间不对,部分线程直接报 java.lang.NumberFormatException:multiple points错,线程直接挂死了。)

Exception in thread "pool-1-thread-4" Exception in thread "pool-1-thread-2" Exception in thread "pool-1-thread-1" java.lang.NumberFormatException: For input string: ""
	at java.lang.NumberFormatException.forInputString(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.text.DigitList.getLong(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Exception in thread "pool-1-thread-5" java.lang.NumberFormatException: multiple points
	at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
	at sun.misc.FloatingDecimal.parseDouble(Unknown Source)
	at java.lang.Double.parseDouble(Unknown Source)
	at java.text.DigitList.getDouble(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
java.lang.NumberFormatException: multiple points
	at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
	at sun.misc.FloatingDecimal.parseDouble(Unknown Source)
	at java.lang.Double.parseDouble(Unknown Source)
	at java.text.DigitList.getDouble(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Exception in thread "pool-1-thread-3" java.lang.NumberFormatException: For input string: ".320194E"
	at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
	at sun.misc.FloatingDecimal.parseDouble(Unknown Source)
	at java.lang.Double.parseDouble(Unknown Source)
	at java.text.DigitList.getDouble(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
java.lang.NumberFormatException: For input string: ""
	at java.lang.NumberFormatException.forInputString(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.text.DigitList.getLong(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Exception in thread "pool-1-thread-17" Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Sun Jun 13 11:30:59 CST 2021
Mon May 13 00:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon Aug 05 03:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 13:30:59 CST 2019
Sun May 13 11:30:59 CST 59
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 13:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 13:59:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Sat May 13 11:30:59 CST 1111
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Exception in thread "pool-1-thread-16" Mon May 13 11:30:59 CST 2019java.lang.NumberFormatException: For input string: ""

	at java.lang.NumberFormatException.forInputString(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)Mon May 13 11:30:59 CST 2019

	at java.lang.Long.parseLong(Unknown Source)
	at java.text.DigitList.getLong(Unknown Source)Mon May 13 11:30:59 CST 2019

	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Exception in thread "pool-1-thread-14" Exception in thread "pool-1-thread-15" java.lang.NumberFormatException: multiple points
	at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
	at sun.misc.FloatingDecimal.parseDouble(Unknown Source)
	at java.lang.Double.parseDouble(Unknown Source)
	at java.text.DigitList.getDouble(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Exception in thread "pool-1-thread-10" java.lang.NumberFormatException: For input string: ""
	at java.lang.NumberFormatException.forInputString(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.text.DigitList.getLong(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Exception in thread "pool-1-thread-13" java.lang.NumberFormatException: For input string: "E.4529019E452"
	at java.lang.NumberFormatException.forInputString(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.text.DigitList.getLong(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Exception in thread "pool-1-thread-9" java.lang.NumberFormatException: For input string: "E.4529019E4"
	at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
	at sun.misc.FloatingDecimal.parseDouble(Unknown Source)
	at java.lang.Double.parseDouble(Unknown Source)
	at java.text.DigitList.getDouble(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Exception in thread "pool-1-thread-6" java.lang.NumberFormatException: For input string: "E.4529019E"
	at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
	at sun.misc.FloatingDecimal.parseDouble(Unknown Source)
	at java.lang.Double.parseDouble(Unknown Source)
	at java.text.DigitList.getDouble(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Exception in thread "pool-1-thread-12" java.lang.NumberFormatException: For input string: ""
	at java.lang.NumberFormatException.forInputString(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.text.DigitList.getLong(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
java.lang.NumberFormatException: For input string: "11."
	at java.lang.NumberFormatException.forInputString(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.lang.Long.parseLong(Unknown Source)
	at java.text.DigitList.getLong(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
java.lang.NumberFormatException: multiple points
	at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)
	at sun.misc.FloatingDecimal.parseDouble(Unknown Source)
	at java.lang.Double.parseDouble(Unknown Source)
	at java.text.DigitList.getDouble(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Wed May 15 11:30:59 CST 2019
Exception in thread "pool-1-thread-20" Mon May 13 11:30:59 CST 2019
java.lang.NumberFormatException: For input string: "."
Mon May 13 11:30:59 CST 2019
	at sun.misc.FloatingDecimal.readJavaFormatString(Unknown Source)Mon May 13 11:30:59 CST 2019

	at sun.misc.FloatingDecimal.parseDouble(Unknown Source)Mon May 13 11:30:59 CST 2019

	at java.lang.Double.parseDouble(Unknown Source)Mon May 13 11:30:59 CST 2019

Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
	at java.text.DigitList.getDouble(Unknown Source)
	at java.text.DecimalFormat.parse(Unknown Source)
	at java.text.SimpleDateFormat.subParse(Unknown Source)
	at java.text.SimpleDateFormat.parse(Unknown Source)
	at java.text.DateFormat.parse(Unknown Source)
	at com.SimpleDateFormatTest.parse(SimpleDateFormatTest.java:19)
	at com.SimpleDateFormatTest.lambda$0(SimpleDateFormatTest.java:29)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)

多线程不安全原因

因为我们吧SimpleDateFormat定义为静态变量,那么多线程下SimpleDateFormat的实例就会被多个线程共享,B线程会读取到A线程的时间,就会出现时间差异和其它各种问题。SimpleDateFormat和它继承的DateFormat类也不是线程安全的

来看看SimpleDateFormat的format()方法的源码

/**
     * Formats the given <code>Date</code> into a date/time string and appends
     * the result to the given <code>StringBuffer</code>.
     *
     * @param date the date-time value to be formatted into a date-time string.
     * @param toAppendTo where the new date-time text is to be appended.
     * @param pos the formatting position. On input: an alignment field,
     * if desired. On output: the offsets of the alignment field.
     * @return the formatted date-time string.
     * @exception NullPointerException if the given {@code date} is {@code null}.
     */
    @Override
    public StringBuffer format(Date date, StringBuffer toAppendTo,
                               FieldPosition pos)
    {
        pos.beginIndex = pos.endIndex = 0;
        return format(date, toAppendTo, pos.getFieldDelegate());
    }

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

        boolean useDateFormatSymbols = useDateFormatSymbols();

        for (int i = 0; i < compiledPattern.length; ) {
            int tag = compiledPattern[i] >>> 8;
            int count = compiledPattern[i++] & 0xff;
            if (count == 255) {
                count = compiledPattern[i++] << 16;
                count |= compiledPattern[i++];
            }

            switch (tag) {
            case TAG_QUOTE_ASCII_CHAR:
                toAppendTo.append((char)count);
                break;

            case TAG_QUOTE_CHARS:
                toAppendTo.append(compiledPattern, i, count);
                i += count;
                break;

            default:
                subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
                break;
            }
        }
        return toAppendTo;
    }

注意 calendar.setTime(date);,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设置的值了,而这就是引发问题的根源,出现时间不对,线程挂死等等。

其实SimpleDateFormat源码上作者也给过我们提示:
在这里插入图片描述
意思就是

日期格式不同步。 建议为每个线程创建单独的格式实例。 如果多个线程同时访问一种格式,则必须在外部同步该格式。


解决方案

好了说了这么多老铁你应该有了一定的了解了吧,来聊一聊解决方案吧!

只在需要的时候创建新实例,不用static修饰,废话不多说上代码:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.text.ParseException;

public class SimpleDateFormatTest {

	//private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	
	// 改造
	public static String formatDate(Date date) throws ParseException {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return sdf.format(date);
	}

	// 改造
	public static Date parse(String strDate) throws ParseException {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return sdf.parse(strDate);
	}

	public static void main(String[] args) throws InterruptedException, ParseException {
		ExecutorService service =Executors.newFixedThreadPool(100);

		for(int i=0;i <20;i++){
	        service.execute(()->{
	            for(int j =0;j <10;j++){
	                try{
	                    System.out.println(parse("2019-05-13 11:30:59"));
	                }catch(ParseException e){
	                    e.printStackTrace();
	                }
	            }
	        });
	    }
	    
	// 等待上述的线程执行完
	service.shutdown();
	service.awaitTermination(1,TimeUnit.DAYS);
	}

}

和上面一样的代码,控制台的输出如下:(老铁神奇么)

Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019

如上代码,仅在需要用到的地方创建一个新的实例,就没有线程安全问题,不过也加重了创建对象的负担,会频繁地创建和销毁对象,效率较低。


synchronized大法好

话不多说上代码:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.text.ParseException;

public class SimpleDateFormatTest {

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

	// 改造
	public static String formatDate(Date date) throws ParseException {
		//SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		//return sdf.format(date);

		synchronized (sdf) {

			return sdf.format(date);

		}
	}

	// 改造
	public static Date parse(String strDate) throws ParseException {
		//SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		//return sdf.parse(strDate);
		
		synchronized (sdf) {

			return sdf.parse(strDate);

		}
	}

	public static void main(String[] args) throws InterruptedException, ParseException {
		ExecutorService service = Executors.newFixedThreadPool(100);

		for (int i = 0; i < 20; i++) {
			service.execute(() -> {
				for (int j = 0; j < 10; j++) {
					try {
						System.out.println(parse("2019-05-13 11:30:59"));
					} catch (ParseException e) {
						e.printStackTrace();
					}
				}
			});
		}

		// 等待上述的线程执行完
		service.shutdown();
		service.awaitTermination(1, TimeUnit.DAYS);
	}

}

控制台输出也是正确的哦:

Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019

简单粗暴,synchronized往上一套也可以解决线程安全问题,缺点自然就是并发量大的时候会对性能有影响,线程阻塞。


ThreadLocal大法

不说废话上代码:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.text.DateFormat;
import java.text.ParseException;

public class SimpleDateFormatTest {

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

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

	// 改造
	public static String formatDate(Date date) {
		// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		// return sdf.format(date);

		/*
		 * synchronized (sdf) {
		 * 
		 * return sdf.format(date);
		 * 
		 * }
		 */

		return threadLocal.get().format(date);
	}

	// 改造
	public static Date parse(String strDate) throws ParseException {
		// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		// return sdf.parse(strDate);

		/*
		 * synchronized (sdf) {
		 * 
		 * return sdf.parse(strDate);
		 * 
		 * }
		 */
		return threadLocal.get().parse(strDate);
	}

	public static void main(String[] args) throws InterruptedException, ParseException {
		ExecutorService service = Executors.newFixedThreadPool(100);

		for (int i = 0; i < 20; i++) {
			service.execute(() -> {
				for (int j = 0; j < 10; j++) {
					try {
						System.out.println(parse("2019-05-13 11:30:59"));
					} catch (ParseException e) {
						e.printStackTrace();
					}
				}
			});
		}

		// 等待上述的线程执行完
		service.shutdown();
		service.awaitTermination(1, TimeUnit.DAYS);
	}

}

控制台输出也是正确的哦:

Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019
Mon May 13 11:30:59 CST 2019

ThreadLocal可以确保每个线程都可以得到单独的一个SimpleDateFormat的对象,那么自然也就不存在竞争问题了。


基于JDK1.8的DateTimeFormatter

也是《阿里巴巴开发手册》给我们的解决方案,对之前的代码进行改造:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.text.ParseException;

public class SimpleDateFormatTest {

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

	// 改造
	public static String formatDate2(LocalDateTime date) {
		return sdf.format(date);
	}

	public static LocalDateTime parse2(String dateNow) {

		return LocalDateTime.parse(dateNow, sdf);

	}

	public static void main(String[] args) throws InterruptedException, ParseException {
		ExecutorService service = Executors.newFixedThreadPool(100);

		// 20个线程
		for (int i = 0; i < 20; i++) {
			service.execute(() -> {
				for (int j = 0; j < 10; j++) {
					try {
						System.out.println(parse2(formatDate2(LocalDateTime.now())));
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			});
		}

		// 等待上述的线程执行完
		service.shutdown();
		service.awaitTermination(1, TimeUnit.DAYS);
	}

}

运行结果就不贴了,不会出现报错和时间不准确的问题。

DateTimeFormatter源码上作者也加注释说明了,他的类是不可变的,并且是线程安全的。

ok,现在是不是可以对你项目里的日期工具类进行一波优化了呢?


到这里使用 SimpleDateFormat?你的项目崩没老铁!分享完毕了!


最后

  • 更多参考精彩博文请看这里:陈永佳的博客

  • 喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈永佳

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值