转载

不简单的 SimpleDateFormat

点击蓝色“ 程序猿DD ”关注我

回复“ 资源 ”获取独家整理的学习资料!

事实证明,Java 的 SimpleDateFormat 并没有那么简单。

格式化和解析日期是个(痛苦的)日常任务。每天,它都让我们很头疼。

在 Java 中格式化和解析日期的一种常见方法是使用 SimpleDateFormat 。下面是我们用到的一个公共类。


 

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Date;


public final class DateUtils {


public static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");


private DateUtils() {}


public static Date parse(String target) {

try {

return SIMPLE_DATE_FORMAT.parse(target);

} catch (ParseException e) {

e.printStackTrace();

}

return null;

}


public static String format(Date target) {

return SIMPLE_DATE_FORMAT.format(target);

}


}

你觉得它会像我们预期的那样进行工作么?让我们试一试。


 

private static void testSimpleDateFormatInSingleThread() {

final String source = "2019-01-11";

System.out.println(DateUtils.parse(source));

}


// Fri Jan 11 00:00:00 IST 2019

是的,它奏效了。接下来再用多线程再试一试。


 

private static void testSimpleDateFormatWithThreads() {

ExecutorService executorService = Executors.newFixedThreadPool(10);


final String source = "2019-01-11";


System.out.println(":: parsing date string ::");

IntStream.rangeClosed(0, 20)

.forEach((i) -> executorService.submit(() -> System.out.println(DateUtils.parse(source))));


executorService.shutdown();

}

这是我得到的结果:


 

:: parsing date string ::

... omitted

Fri Jan 11 00:00:00 IST 2019Sat Jul 11 00:00:00 IST 2111Fri Jan 11 00:00:00 IST 2019

... omitted

结果很有意思,不是么?这是我们大多数人在 Java 中格式化日期时常犯的错误。为什么?因为我们不了解线程安全。以下是 Java doc 中关于 SimpleDateFormat   的内容:

日期格式是不同步的。

建议为每个线程创建独立的格式实例。

如果多个线程同时访问一个格式,则它必须是外部同步的。

Tip:当我们使用实例变量时,应始终检查其是否是一个线程安全类。

正如文档所述,我们为每个线程持有一个独立的变量来解决该问题。如果我们想共享对象?有什么解决方案?

方案一:ThreadLocal

这个问题可以通过使用 ThreadLocal   变量来解决。 ThreadLocal    get()   方法将为我们提供当前线程的正确值。


 

import java.text.ParseException;

import java.text.SimpleDateFormat;

import java.util.Date;


public final class DateUtilsThreadLocal {


public static final ThreadLocal SIMPLE_DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));


private DateUtilsThreadLocal() {}


public static Date parse(String target) {

try {

return SIMPLE_DATE_FORMAT.get().parse(target);

} catch (ParseException e) {

e.printStackTrace();

}

return null;

}


public static String format(Date target) {

return SIMPLE_DATE_FORMAT.get().format(target);

}

}

方案二:Java 8 中线程安全的日期时间 API

Java 8 引入了一套新的日期时间 API。我们有一个更好的、麻烦更少的 SimpleDateFormat   替代品。如果我们真的需要坚持使用   SimpleDateFormat ,可以继续使用   ThreadLocal 。但是当有更好的选择时,我们应考虑使用它。

Java 8 引入了几个线程安全的日期类。

以下是 Java doc 的描述:

本类是不可变的,且线程安全的。

这些类是更加值得研究的,包括 DateTimeFormatter,[OffsetDateTime], ZonedDateTime,LocalDateTime ,LocalDate 和 LocalTime。

我们的解决方案:


 

import java.time.LocalDate;

import java.time.format.DateTimeFormatter;


public class DateUtilsJava8 {


public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");


private DateUtilsJava8() {}


public static LocalDate parse(String target) {

return LocalDate.parse(target, DATE_TIME_FORMATTER);

}


public static String format(LocalDate target) {

return target.format(DATE_TIME_FORMATTER);

}


}

3. 结论

Java 8 的解决方案使用不可变类,这是解决多线程问题的好方法。不可变类本质上是线程安全的,所以请尽可能地使用它们。

Happy coding!

留言交流不过瘾? 添加微信: zyc_enjoy

根据指引加入各种主题讨论群

推荐阅读

  • 这几款好用超赞的 Google Chrome插件送给你!

  • IntelliJ IDEA 2019.2最新解读

  • Spring Cloud与Dubbo的完美融合之手

  • 狡猾的 AI 工程师,编个故事骗走 2 亿人民币...

  • 日均7亿交易量,如何设计高可用的MySQL架构?

签到计划

活动介绍 : 自律到极致-人生才精致:第13期

活动奖励: 《Spring Cloud微服务: 入门、实战与进阶》 x 10

扫描下方二维码, 签到参与

不简单的 SimpleDateFormat

推荐关注:锅外的大佬

不简单的 SimpleDateFormat

原文  http://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&mid=2247488183&idx=2&sn=a94433cd7e60852fb5131fb6dfb045e5
正文到此结束
Loading...