SimpleDateFormat是线程不安全的类,一般不要定义成static变量。


package com.kris;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.*;

/**
 * @Author kris
 * @Discription todo
 * @Date 2020/7/23
 * @Classname DateFormat
 * @Version
 **/
public class DateFormat {
        /**     * 定义一个全局的 SimpleDateFormat     */
        private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss");
        /**     * 使用 ThreadFactoryBuilder 定义一个线程池     */
        private static ExecutorService pool = new ThreadPoolExecutor(10, 10,        60L, TimeUnit.SECONDS,        new ArrayBlockingQueue(100));
        /**     * 定义一个 CountDownLatch,保证所有子线程执行完之后主线程再执行     */
        private static CountDownLatch countDownLatch = new CountDownLatch(100);

        public static void main(String[] args) throws Exception{
            // 定义一个线程安全的 HashSet
            Set<String> dates = Collections.synchronizedSet(new HashSet<String>());
            for (int i = 0; i < 100; i++) {

                // 获取当前时间
                Calendar calendar = Calendar.getInstance();
                int finalI = i;
                pool.execute(() -> {
                    // 时间增加
                    calendar.add(Calendar.DATE, finalI);
                    //通过 simpleDateFormat 把时间转换成字符串
                    String dateString = simpleDateFormat.format(calendar. getTime());
                    // 把字符串放入 Set 中
                    dates.add(dateString);
                    //countDown
                    countDownLatch.countDown();
                });
            }
            // 阻塞,直到 countDown 数量为 0
            countDownLatch.await();
            // 输出去重后的时间个数
            System.out.println(dates.size());
            System.out.println(dates.toString());
        }
}

 

结果

87
[2020-10-03 13:31:38, 2020-09-07 13:31:38, 2020-08-07 13:31:38, 2020-09-29 13:31:38, 2020-09-10 13:31:38, 2020-08-29 13:31:38, 2020-08-01 13:31:38, 2020-07-29 13:31:38, 2020-09-04 13:31:38, 2020-09-13 13:31:38, 2020-10-19 13:31:38, 2020-09-01 13:31:38, 2020-09-05 13:31:38, 2020-10-04 13:31:38, 2020-10-10 13:31:38, 2020-09-28 13:31:38, 2020-10-16 13:31:38, 2020-10-27 13:31:38, 2020-08-17 13:31:38, 2020-08-16 13:31:38, 2020-09-22 13:31:38, 2020-10-22 13:31:38, 2020-08-22 13:31:38, 2020-09-16 13:31:38, 2020-08-23 13:31:38, 2020-07-23 13:31:38, 2020-09-23 13:31:38, 2020-09-17 13:31:38, 2020-08-11 13:31:38, 2020-09-11 13:31:38, 2020-10-09 13:31:38, 2020-10-21 13:31:38, 2020-08-28 13:31:38, 2020-08-05 13:31:38, 2020-10-15 13:31:38, 2020-08-18 13:31:38, 2020-10-23 13:31:38, 2020-08-15 13:31:38, 2020-09-21 13:31:38, 2020-10-20 13:31:38, 2020-09-24 13:31:38, 2020-08-21 13:31:38, 2020-09-15 13:31:38, 2020-10-14 13:31:38, 2020-10-29 13:31:38, 2020-08-03 13:31:38, 2020-07-27 13:31:38, 2020-09-06 13:31:38, 2020-10-08 13:31:38, 2020-10-17 13:31:38, 2020-09-12 13:31:38, 2020-10-26 13:31:38, 2020-09-03 13:31:38, 2020-08-30 13:31:38, 2020-10-05 13:31:38, 2020-09-30 13:31:38, 2020-08-27 13:31:38, 2020-10-02 13:31:38, 2020-09-27 13:31:38, 2020-08-24 13:31:38, 2020-09-18 13:31:38, 2020-10-11 13:31:38, 2020-07-30 13:31:38, 2020-08-09 13:31:38, 2020-09-02 13:31:38, 2020-07-25 13:31:38, 2020-08-31 13:31:38, 2020-09-08 13:31:38, 2020-10-07 13:31:38, 2020-08-25 13:31:38, 2020-07-31 13:31:38, 2020-10-01 13:31:38, 2020-09-25 13:31:38, 2020-08-13 13:31:38, 2020-10-13 13:31:38, 2020-08-20 13:31:38, 2020-10-12 13:31:38, 2020-08-26 13:31:38, 2020-09-26 13:31:38, 2020-10-18 13:31:38, 2020-09-20 13:31:38, 2020-08-19 13:31:38, 2020-09-19 13:31:38, 2020-09-14 13:31:38, 2020-08-14 13:31:38, 2020-10-06 13:31:38, 2020-10-30 13:31:38]

 

SimpleDateFormat 中的 format 方法在执行过程中,会使用一个成员变量
calendar 来保存时间。这其实就是问题的关键。
由于我们在声明 SimpleDateFormat 的时候,使用的是 static 定义的。那么
这个 SimpleDateFormat 就是一个共享变量,随之,SimpleDateFormat 中的
calendar 也就可以被多个线程访问到。
假设线程1刚刚执行完calendar.setTime 把时间设置成 2018-11-11,还
没等执行完,线程2又执行了calendar.setTime 把时间改成了 2018-12-12。
这时候线程 1 继续往下执行,拿到的 calendar.getTime 得到的时间就是线程 2 改
过之后的。
除了 format 方法以外,SimpleDateFormat 的 parse 方法也有同样的问题。
所以,不要把 SimpleDateFormat 作为一个共享变量使用

解决方案

1,方案一

pool.execute(() -> {
                   //simpleDateFormat在这里设置成局部变量
                    // 时间增加
                    calendar.add(Calendar.DATE, finalI);
                    //通过 simpleDateFormat 把时间转换成字符串
                    String dateString = simpleDateFormat.format(calendar. getTime());
                    // 把字符串放入 Set 中
                    dates.add(dateString);
                    //countDown
                    countDownLatch.countDown();
});

 2,方案二 给simpleDateFormat加锁

pool.execute(() -> {
                    synchronized (simpleDateFormat){
                        // 时间增加
                        calendar.add(Calendar.DATE, finalI);
                        //通过 simpleDateFormat 把时间转换成字符串
                        String dateString = simpleDateFormat.format(calendar. getTime());
                        // 把字符串放入 Set 中
                        dates.add(dateString);
                        //countDown
                        countDownLatch.countDown();
                    }
                });

 3,方案三 使用ThreadLocal

/** * 使用 ThreadLocal 定义一个全局的 SimpleDateFormat */
        private static ThreadLocal<SimpleDateFormat> simpleDateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
            @Override
            protected SimpleDateFormat initialValue() {
                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }
        };

 使用的时候

pool.execute(() -> {
                        // 时间增加
                        calendar.add(Calendar.DATE, finalI);
                        //通过 simpleDateFormat 把时间转换成字符串
                        //String dateString = simpleDateFormat.format(calendar. getTime());
                        String dateString = simpleDateFormatThreadLocal.get().format(calendar.getTime());
                        // 把字符串放入 Set 中
                        dates.add(dateString);
                        //countDown
                        countDownLatch.countDown();
                });

 4使用 DateTimeFormatter
如果是 Java8 应用,可以使用 DateTimeFormatter 代替 SimpleDateFormat,这是一个线程安全的格式化工具类。
// 解析日期

String dateStr= "2016 年 10 月 25 日 "; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
 LocalDate date= LocalDate.parse(dateStr, formatter);

 // 日期转换为字符串

LocalDateTime now = LocalDateTime.now(); 
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy 年MM 月dd 日 hh:mm a");
String nowStr = now .format(format);

 

 

   

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM