岁月沉香:品味成熟女性的别样风情
SimpleDateFormat是线程不安全的主要原因如下
内部状态可变性// SimpleDateFormat 内部维护了可变状态 private StringBuffer format(Date date, StringBuffer toAppendTo, FieldDelegate delegate) { // 会修改内部的 Calendar 对象 calendar.setTime(date); // ... }
共享的 Calendar 实例每个SimpleDateFormat对象内部都持有一个Calendar实例public class SimpleDateFormat extends DateFormat { protected Calendar calendar; // 共享的可变状态 public String format(Date date) { //
设置时间到 calendar calendar.setTime(date); //
使用 calendar 进行格式化 return format(calendar); } }
并发问题场景情况1多线程同时调用 format()SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd); // 线程A sdf.format(dateA); // 设置 calendar 为 dateA // 线程B在此刻插入 sdf.format(dateB); // 设置 calendar 为 dateB // 线程A继续格式化但calendar已经被线程B修改情况2多线程同时调用 parse()// 线程A sdf.parse(
-
; // 线程B sdf.parse(
-
; // 两者可能互相干扰得到错误结果
问题复现代码SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd HH:mm:ss); ExecutorService executor Executors.newFixedThreadPool(
; ListFutureString futures new ArrayList(); for (int i 0; i 10; i) { final int index i; futures.add(executor.submit(() - { Date date new Date(System.currentTimeMillis() index *
; return sdf.format(date); // 可能出现空指针、格式错误、时间错乱 })); }
线程安全的替代方案方案1使用 ThreadLocal推荐private static final ThreadLocalSimpleDateFormat threadLocal ThreadLocal.withInitial(() - new SimpleDateFormat(yyyy-MM-dd)); public String formatDate(Date date) { return threadLocal.get().format(date); }方案2每次创建新实例public String formatDate(Date date) { return new SimpleDateFormat(yyyy-MM-dd).format(date); } // 缺点频繁创建对象性能较差方案3使用 DateTimeFormatterJava 8// DateTimeFormatter 是线程安全的 DateTimeFormatter formatter DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss); // 格式化 String formatted LocalDateTime.now().format(formatter); // 解析 LocalDateTime parsed LocalDateTime.parse(
10:30:00, formatter);方案4使用 FastDateFormatApache Commons LangFastDateFormat formatter FastDateFormat.getInstance(yyyy-MM-dd); String formatted formatter.format(new Date()); // 线程安全
为什么 DateTimeFormatter 线程安全public final class DateTimeFormatter { // 所有字段都是 final 的 private final CompositePrinter printer; private final CompositeParser parser; private final Locale locale; // 所有方法都是纯函数不修改内部状态 public String format(TemporalAccessor temporal) { // 不修改任何实例变量 } }
总结根本原因SimpleDateFormat内部可变状态Calendar在多线程下被共享修改解决方案使用ThreadLocal包装适合传统项目使用 Java 8 的DateTimeFormatter推荐新项目使用同步锁性能差不推荐在并发环境下永远不要共享同一个SimpleDateFormat实例。
天美麻花星空大全在线观看免费-天美麻花星空大全在线观看免费应用