Re: https://mp.weixin.qq.com/s/i4BdU5vsN_AEBK-QRxQF0w

一、SimpleDateFormat

1.1 日期转换

  1. Date data = new Date();
  2. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  3. String dataStr = sdf.format(data);
  4. System.out.println(dataStr);
  5. System.out.println(sdf.parse(dataStr));

日期格式:
image.png
元素解释:
image.png

1.2 线程不安全

多线程访问同一个静态 SimpleDateFormat 不安全:

/** * @author Hollis */ 
public class Main {

   /**
    * 定义一个全局的SimpleDateFormat
    */
   private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

   /**
    * 使用ThreadFactoryBuilder定义一个线程池
    */
   private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
       .setNameFormat("demo-pool-%d").build();

   private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
       0L, TimeUnit.MILLISECONDS,
       new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

   /**
    * 定义一个CountDownLatch,保证所有子线程执行完之后主线程再执行
    */
   private static CountDownLatch countDownLatch = new CountDownLatch(100);

   public static void main(String[] args) {
       //定义一个线程安全的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());
   }
}

原因在于 format 方法使用了类的成员变量:
image.png
parse 有同样的问题:
image.png
image.png

1.3 解决线程安全问题

使用局部变量

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

给 SimpleDateFormat 对象加锁

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

使用 ThreadLocal

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

//用法
String dateString = simpleDateFormatThreadLocal.get().format(calendar.getTime());

使用 DateTimeFormatter(Java8)

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

//日期转换为字符串
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm a");
String nowStr = now.format(formatter);
System.out.println(nowStr);

[

](https://mp.weixin.qq.com/s/i4BdU5vsN_AEBK-QRxQF0w)