详解Java中日期格式化的实现方法与潜在问题解决

 更新时间:2025年11月28日 08:41:31   作者:uup  
这篇文章主要为大家详细介绍了Java中日期格式化的实现方法与潜在问题解决的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

一、Bug 场景

在一个电商系统中,需要将用户下单时间以特定格式展示给用户,同时在后台也会基于这个格式化后的时间进行一些数据统计和分析。开发人员使用 SimpleDateFormat 类对日期进行格式化操作。

二、代码示例

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatBug {
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy - MM - dd HH:mm:ss");

    public static void main(String[] args) {
        // 模拟获取到的订单时间字符串
        String orderTimeStr = "2023 - 10 - 15 14:30:00";

        try {
            Date orderDate = dateFormat.parse(orderTimeStr);
            System.out.println("解析后的日期: " + dateFormat.format(orderDate));
        } catch (ParseException e) {
            System.out.println("日期解析错误: " + e.getMessage());
        }


        // 多线程环境下测试
        Thread thread1 = new Thread(() -> {
            try {
                Date date1 = dateFormat.parse("2023 - 10 - 16 10:00:00");
                System.out.println("线程 1 解析后的日期: " + dateFormat.format(date1));
            } catch (ParseException e) {
                System.out.println("线程 1 日期解析错误: " + e.getMessage());
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                Date date2 = dateFormat.parse("2023 - 10 - 17 15:30:00");
                System.out.println("线程 2 解析后的日期: " + dateFormat.format(date2));
            } catch (ParseException e) {
                System.out.println("线程 2 日期解析错误: " + e.getMessage());
            }
        });

        thread1.start();
        thread2.start();
    }
}

三、问题描述

单线程情况:乍一看,代码在单线程环境下运行良好,能正确地将字符串解析为 Date 对象并格式化输出。例如,上述代码在单线程执行 main 方法时,输出结果符合预期:

解析后的日期: 2023 - 10 - 15 14:30:00

多线程情况:然而,在多线程环境下,SimpleDateFormat 不是线程安全的。当多个线程同时使用同一个 SimpleDateFormat 实例进行日期解析和格式化操作时,可能会出现不可预测的结果,比如抛出 ParseException 或者得到错误的格式化日期。在上述代码中启动 thread1 和 thread2 后,可能会出现以下错误输出:

线程 1 日期解析错误: Unparseable date: "2023 - 10 - 16 10:00:00"
线程 2 日期解析错误: Unparseable date: "2023 - 10 - 17 15:30:00"

这是因为 SimpleDateFormat 内部维护了一些状态信息,如日历对象等,多个线程并发访问和修改这些状态时会相互干扰,导致解析或格式化失败。

四、解决方案

线程局部变量(ThreadLocal) :可以使用 ThreadLocal 为每个线程创建独立的 SimpleDateFormat 实例,避免多线程之间的状态干扰。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatBugFixed {
    private static final ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy - MM - dd HH:mm:ss"));

    public static void main(String[] args) {
        // 模拟获取到的订单时间字符串
        String orderTimeStr = "2023 - 10 - 15 14:30:00";

        try {
            Date orderDate = dateFormatThreadLocal.get().parse(orderTimeStr);
            System.out.println("解析后的日期: " + dateFormatThreadLocal.get().format(orderDate));
        } catch (ParseException e) {
            System.out.println("日期解析错误: " + e.getMessage());
        }


        // 多线程环境下测试
        Thread thread1 = new Thread(() -> {
            try {
                Date date1 = dateFormatThreadLocal.get().parse("2023 - 10 - 16 10:00:00");
                System.out.println("线程 1 解析后的日期: " + dateFormatThreadLocal.get().format(date1));
            } catch (ParseException e) {
                System.out.println("线程 1 日期解析错误: " + e.getMessage());
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                Date date2 = dateFormatThreadLocal.get().parse("2023 - 10 - 17 15:30:00");
                System.out.println("线程 2 解析后的日期: " + dateFormatThreadLocal.get().format(date2));
            } catch (ParseException e) {
                System.out.println("线程 2 日期解析错误: " + e.getMessage());
            }
        });

        thread1.start();
        thread2.start();
    }
}

使用 Java 8 的 DateTimeFormatter:Java 8 引入的 DateTimeFormatter 是线程安全的。可以使用它来替代 SimpleDateFormat

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeFormatterExample {
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy - MM - dd HH:mm:ss");

    public static void main(String[] args) {
        // 模拟获取到的订单时间字符串
        String orderTimeStr = "2023 - 10 - 15 14:30:00";

        LocalDateTime orderDateTime = LocalDateTime.parse(orderTimeStr, formatter);
        System.out.println("解析后的日期: " + orderDateTime.format(formatter));


        // 多线程环境下测试
        Thread thread1 = new Thread(() -> {
            LocalDateTime date1 = LocalDateTime.parse("2023 - 10 - 16 10:00:00", formatter);
            System.out.println("线程 1 解析后的日期: " + date1.format(formatter));
        });

        Thread thread2 = new Thread(() -> {
            LocalDateTime date2 = LocalDateTime.parse("2023 - 10 - 17 15:30:00", formatter);
            System.out.println("线程 2 解析后的日期: " + date2.format(formatter));
        });

        thread1.start();
        thread2.start();
    }
}

到此这篇关于详解Java中日期格式化的实现方法与潜在问题解决的文章就介绍到这了,更多相关Java日期格式化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring事件监听机制观察者模式详解

    Spring事件监听机制观察者模式详解

    这篇文章主要为大家介绍了Spring事件监听机制观察者模式实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Feign的原理及概念详解

    Feign的原理及概念详解

    文章介绍了Feign,一个声明式、模板化的HTTP客户端,以及其在Spring Cloud中的增强,Feign通过代理类简化HTTP请求的调用,并提供了详细的调用流程、优化配置和架构说明,帮助开发者更方便地进行服务调用和管理
    2025-11-11
  • 使用maven对springboot项目进行瘦身分离jar的多种处理方案

    使用maven对springboot项目进行瘦身分离jar的多种处理方案

    springboot项目打包一般我们都使用它自带的spring-boot-maven-plugin插件,这个插件默认情况下,会把所有的依赖包全部压缩到一个jar里面,今天给大家分享几种方案来如何减小我们的打包文件,需要的朋友可以参考下
    2024-02-02
  • 详解Spring框架注解扫描开启之配置细节

    详解Spring框架注解扫描开启之配置细节

    本篇文章主要介绍了详解Spring框架注解扫描开启之配置细节,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • java 过滤器filter防sql注入的实现代码

    java 过滤器filter防sql注入的实现代码

    下面小编就为大家带来一篇java 过滤器filter防sql注入的实现代码。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • HashMap线程不安全问题解析

    HashMap线程不安全问题解析

    这篇文章主要介绍了HashMap线程不安全问题解析,HashMap的线程不安全体现在会造成死循环、数据丢失、数据覆盖等问题,其中死循环和数据丢失是在JDK1.7中出现的问题,在JDK1.8中已经得到解决,但是1.8中仍会有数据覆盖这样的问题,需要的朋友可以参考下
    2023-11-11
  • Spring计时器StopWatch使用示例

    Spring计时器StopWatch使用示例

    这篇文章主要介绍了Spring计时器StopWatch使用示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • Java中用内存映射处理大文件的实现代码

    Java中用内存映射处理大文件的实现代码

    下面小编就为大家带来一篇Java中用内存映射处理大文件的实现代码。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • 设置tomcat启用gzip压缩的具体操作方法

    设置tomcat启用gzip压缩的具体操作方法

    如果发现内容没有被压缩,可以考虑调整compressionMinSize大小,如果请求资源小于这个数值,则不会启用压缩
    2013-08-08
  • Spring Boot集成Shiro实现动态加载权限的完整步骤

    Spring Boot集成Shiro实现动态加载权限的完整步骤

    这篇文章主要给大家介绍了关于Spring Boot集成Shiro实现动态加载权限的完整步骤,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring Boot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09

最新评论