SimpleDateFormat使用

在日常开发中,我们经常会用到时间,我们有很多办法在Java代码中获取时间。但是不同的方法获取到的时间的格式都不尽相同,这时候就需要一种格式化工具,把时间显示成我们需要的格式。最常用的方法就是使用SimpleDateFormat类。

SimpleDateFormat是Java提供的一个格式化和解析日期的工具类。它允许进行格式化(日期 -> 文本)、解析(文本 -> 日期)和规范化。SimpleDateFormat使得可以选择任何用户定义的日期- 时间格式的模式。在Java中,可以使用SimpleDateFormat的format方法,将一个Date类型转化成String 类型,并且可以指定输出格式。比如:

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date = new Date();
    System.out.println(simpleDateFormat.format(date));

由于SimpleDateFormat比较常用,而且在一般情况下,一个项目中的时间显示模式都是一样的,所以很多人经常使用如下方式定义SimpleDateFormat:

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

    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(simpleDateFormat.format(date));
    }

这是一个看上去功能比较简单的类,初看没什么问题,但是SimpleDateFormat是线程不安全的,如果真这么使用会有大问题。下面做个测试,测试代码如下:

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

    private static final ThreadFactory THREAD_FACTORY = new ThreadFactoryBuilder().setNameFormat("date-test-pool-%d")
            .build();
    private static final ExecutorService executorService = new ThreadPoolExecutor(10, 10, 60L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10), THREAD_FACTORY);

    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(simpleDateFormat.format(date));
        Set<String> dates = Collections.synchronizedSet(new HashSet<String>());
        for (int i = 0; i < 100; i++) {
            Calendar calendar = Calendar.getInstance();
            int finalI = i;
            executorService.execute(() -> {
                calendar.add(Calendar.DATE, finalI);
                String dateString = simpleDateFormat.format(calendar.getTime());
                dates.add(dateString);
            });
        }
        System.out.println(dates.size());
    }

这个代码也简单,就是循环100次,每次日期都加不同的天数。正常情况下,是100个不同天数的。下面来看看运行结果:
在这里插入图片描述
可以看出,只有72个不同日期。这是怎么回事呢?下面来看看SimpleDateFormat的源码
在这里插入图片描述
在这里插入图片描述
可以看到,问题出在这里,即SimpleDateFormat中的format方法在执行过程中,会使用一个成员变量calendar来保存时间,而calendar并不是线程安全的,因此引起了线程安全问题。

那么我们要如何避免这种问题出现呢?一是SimpleDateFormat不要设置为静态变量,而应该使用局部变量。或者在使用SimpleDateFormat的时候加同步锁。