Java 中SimpleDateFormat 错误用法及改正
创始人
2025-05-30 15:51:55

      • 正确用法 1. 每次都 new
      • 正确用法 2. 加锁
      • 正确用法 3. 使用 ThreadLocal 容器
      • 正确用法4. 改用 DateTimeFormatter(推荐)

开发 Java 项目时经常操作时间、日期与字符串的互相转换,最常见简单的方式是使用 SimpleDateFormat,想必大家对它不陌生。 虽然它简单易用,如果没有正确使用,在一般环境下使用通常不会出错,但在高并发(Highly Concurrent)的环境下就可能会出现异常。

在这里插入图片描述

我们都知道在程序中应尽量少使用 ,因为若频繁实例化,则需要花费较多的成本,因此我们尽可能共用同一个实例。 假设有一个转换日期时间的代码如下:

public class DateUtil {private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");public static String format(Date date) {return simpleDateFormat.format(date);}
}

不幸的是, 就是最典型的错误用法。 官方文件提到:共用 SimpleDateFormat

Date formats are not synchronized. It is recommended to create
separate format instances for each thread. If multiple threads access
a format concurrently, it must be synchronized externally.

从 SimpleDateFormat 的源代码中也可以看到它是有状态的,而且其中 calendar 被宣告为成员变量,因此呼叫, 等 method 时会多次访问此 calendar。 在高并发环境下,将会造成资源竞争,结果值就会不符预期,甚至抛出异常。

幸运的是,已有许多解决方案:

正确用法 1. 每次都 new

public class DateUtil {public static String format(Date date) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");return simpleDateFormat.format(date);}
}

这是最简单的做法,只要每次都宣告区域变量就可以了,区域变量是 thread-safe。 若专案对于效能要求不高,也许可以考虑这个解法,或直到出现效能问题时再考虑其他方法。 毕竟至少这个做法能正确运作,而且简单的作法往往是较好的。

正确用法 2. 加锁

public class DateUtil {private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");synchronized public static String format(Date date) {return simpleDateFormat.format(date);}
}

首先宣告 SimpleDateFormat field,避免重复 new 而造成性能问题。 再加上关键字 就能确保同一时刻只有一个 thread 能执行 (mutual exclusion)。 虽然这个方式不会出错,但可能降低并发度。synchronizedformat

正确用法 3. 使用 ThreadLocal 容器

ThreadLocal 容器是一种让程序达到 thread-safety 的手段,它相当于给每个 thread 都开了一个独立的存储空间,既然 thread 之间互相隔离,自然解决了 race condition 的问题,也让 thread 能重复使用 SimpleDateFormat 实例。 代码如下:

public class DateUtil {// 可以把 ThreadLocal 視為一個全域 Map,key 就是 current thread// 意義上相當於 currentThread 專屬、獨立的 cache。private static ThreadLocal local = new ThreadLocal<>();private static SimpleDateFormat getDateFormat() {// currentThread 從自己的 ThreadLocalMap 取得 SimpleDateFormat。// 如果是 null,則建立 SimpleDateFormat 並放入自己的 ThreadLocalMap 中。SimpleDateFormat dateFormat = local.get();if (dateFormat == null) {dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");local.set(dateFormat);}return dateFormat;}public static String format(Date date) {return getDateFormat().format(date);}
}

举例来说,如果thread pool有10个thread,程序就会建立10个SimpleDateFormat实例,这些thread们在每次的任务中重复使用各自的SimpleDateFormat。 但要注意一点,该 thread 能够重复被使用(例如 server 在处理完一次 request 后,thread 会再回到 thread pool 待命),否则效果会和方法1差不多。 这个方法的缺点是程序会变得较复杂。

正确用法4. 改用 DateTimeFormatter(推荐)

虽然有点文不对题,毕竟这个问题困扰很多人许久了,因此在 Java 8 版本后官方就提供了 对象用来代替 。 就像官方文件中说的:DateTimeFormatterSimpleDateFormat

DateTimeFormatter in Java 8 is immutable and thread-safe alternative to SimpleDateFormat.

简单的演示例如下:

将字符串转成 LocalDate

String dateStr = "2022/05/24";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date = LocalDate.parse(dateStr, formatter);

LocalDateTime 转成字符串

LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm");
System.out.println(now.format(formatter));

相关内容

热门资讯

Docker等容器技术如何与移... 移动应用程序的开发面临着很多挑战,包括开发环境的设置、测试的困难、部署的复杂性等。由于...
【微服务】—— Nacos安装... 文章目录1. Windows安装1.1 下载安装包1.2 解压1.3 端口配置1.4 启动1.5 访...
【OpenGL】 为了理解这个函数我们需要先学习一些OpenGL的内容 OpenGL可视化 https://g...
hjr-详细说一下Redis集... Redis作用 缓存 一般我们用Redis做缓存,热点数据 击穿:访问到...
【蓝桥杯】 C++ 数字三角形... 文章目录题目描述输入描述输出描述实现代码解题思路注意点知识点 题目描述 上图给出了一个数字三角形。从...
VR全景展会丨探索未来,重塑现... 随着科技的不断发展,虚拟现实(VR)技术逐渐成为一个重要的...
C++数据类型 目录 C++基础数据类型 指针 指针类型 指针赋值 引用 参考:《深...
超实用!!! 三分钟将你的项目... 文章目录前言一、在项目中新增配置二、配置github page setting?三、如...
数据结构---队列 专栏:数据结构 个人主页:HaiFan. 专栏简介:这里是...
数字操作方法 系列文章目录 前端系列文章——传送门 JavaScript系列文章——传送门 文章目录系列文章目录...
Cartesi 2023 年 ... 查看 Cartesi Machine、Cartesi Rollups 和 Noether 的更新正在...
JavaWeb——jsp概述入... JSP定义:  在如下一个jsp文件里面有如下的代码  <%@ page content...
一切喜怒哀乐都来自于你的认知 01 有个学子,准备出国,父母请来清华的教授宁向东。请问教授࿱...
JAVA并发编程——synch... 引言         Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,...
git学习----3.21 未... 文章目录前言Git :一个分布式版本控制工具目标一、概述1.1 开发中的实际场景1.2...
Qt优秀开源项目之十七:QtP... QtPromise是Promises/A+规范的Qt/C++实现。该规范的译...
【前端八股文】JavaScri... 文章目录Set概念与arr的比较属性和方法并集、交集、差集Map概念属性和方法String用索引值和...
海康硬盘录像机接入RTSP/o... EasyNVR安防视频云服务平台可支持设备通过RTSP/Onvif协议接入平台,能提供...
在混合劳动力时代如何避免网络安... 在混合劳动力时代如何避免安全网络风险 三年多来,混合工作一直是工作生活中不可或缺的一...
2023还不懂Jmeter接口... 这里介绍的Jmeter接口测试的的实战,如果文章内容没遇看懂的话,我这边...
基于4G/5G弱网聚合的多链路... 基于4G/5G多卡聚合(弱网聚合)的智能融合通信设备技术亮点 增强带宽提供可靠连接 通过将多个有线和...
如何使用Synplify综合v... 文章目录使用Synplify综合的好处synplify的教程方法1(无效)...
2023年全国最新高校辅导员精... 百分百题库提供高校辅导员考试试题、辅导员考试预测题、高校辅导员考试真题、辅导员证考试题库等ÿ...
2022年18个值得期待的Le... 有数百个独特的LearnDash附加组件,您可能很难选择您的LearnDash LMS...
【java基础】Stream流... 文章目录基本介绍流的创建流的各种常见操作forEach方法filter方法map方法peek方法fl...
javaweb高校行政办公自动... 本课题基于我国高校管理信息化建设现状,结合在实际工作中所遇到的问题和收获,...
一款专门为自动化测试打造的集成... 你好,我是不二。 随着行业内卷越来越严重,自动化测试已成为测试工程师的...
【go-zero】golang... 一、casbin 概览 1、casbin基本了解 casbin的GitHub:https://git...
现在开发低代码平台算晚吗? 现在开发低代码平台算晚吗?作为低代码的亲戚——零代码厂商,这篇就以“厂商...
【JavaWeb】书城项目(2... 222.书城项目-第三阶段:修改所有html页面为jsp页面 改成jsp页面之后&#x...