点击左上角蓝字,关注“锅外的大佬”
专注分享国外最新技术内容
格式化和解析日期是个(痛苦的)日常任务。每天,它都让我们很头疼。
在 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
的 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。我们有一个更好的、麻烦更少的 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); } }
Java 8 的解决方案使用不可变类,这是解决多线程问题的好方法。不可变类本质上是线程安全的,所以请尽可能地使用它们。
Happy coding!
spring for all翻译组
8月福利来袭!关注公众号
后台回复:003 ,领取7月翻译集锦~
往期福利回复:001, 002即可领取
●Top11 构建和测试API的工具
●Spring Boot 面试的十个问题
●开车!Spring Batch 入门级示例教程!
右上角按钮分享给更多人哦~
来都来了,点个在看再走吧~~~