SimpleDataFormat 非线程安全
目录
前言
正文
1.出现异常
2.解决方法1
3.解决方法2
总结
前言
SimpleDateFormat
类是 Java 中处理日期和时间格式化和解析的类,但它并不是线程安全的。这意味着多个线程不能安全地共享一个SimpleDateFormat
实例进行日期和时间的解析和格式化。当多个线程共享同一个SimpleDateFormat
实例时,会因为SimpleDateFormat
内部维护的日历字段(例如:Calendar
对象)等的竞争条件而导致解析和格式化错误。
正文
类 SimpleDataFormat 的可以对日期进行解析与格式化,但在使用时如果不想使用 0 进行填充,比如 2000-01-02 只想转换成 2002-1-2 ,我们需要在代码上进行处理,示例代码如下。
package org.example.SimpleDataFormat;import java.text.ParseException;
import java.text.SimpleDateFormat;public class Run {public static void main(String[] args) throws ParseException {String dataString1 = "2000-1-1";String dataString2 = "2000-11-18";SimpleDateFormat format1 = new SimpleDateFormat("yyyy-M-d");SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd");//先按照日期模式将字符串解析成日期再格式化成时间字符串。System.out.println(format1.format(format1.parse(dataString1)));System.out.println(format2.format(format2.parse(dataString1)));System.out.println(format1.format(format1.parse(dataString2)));System.out.println(format2.format(format2.parse(dataString2)));}
}
打印结果如下:
但 SimpleDateFormat 在多线程环境中使用类容易造成数据转换及处理不准确,因为类 SimpleDateFormat 并不是线程安全的。
1.出现异常
本示例将展示使用类 SimpleDataFormat 在多线程环境中处理日期时得到错误结果,这也是在多线程环境中开发经常遇到的问题。
ackage org.example.SimpleDataFormat;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class formatError {static class MyThread extends Thread {private SimpleDateFormat sdf;private String dateString;public MyThread(SimpleDateFormat sdf, String dateString) {this.sdf = sdf;this.dateString = dateString;}@Overridepublic void run() {try {Date dateRef = sdf.parse(dateString);String newDataString = sdf.format(dateRef);if (!newDataString.equals(dateString)) {System.out.println("ThreadName=" + this.getName()+ "报错了 日期字符串:" + dateString + " 转换成的日期为:"+ newDataString);}} catch (ParseException e) {e.printStackTrace();}}}public static void main(String[] args) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");String[] dateStringArray = new String[]{"2000-01-01","2000-01-02","2000-01-03","2000-01-04","2000-01-05","2000-01-06","2000-01-07","2000-01-08","2000-01-09","2000-01-10",};MyThread[] threads = new MyThread[10];for (int i = 0; i < 10; i++) {threads[i] = new MyThread(sdf,dateStringArray[i]);}for (int i = 0; i < 10; i++) {threads[i].start();}}
}
运行结果如图:
从打印的结果来看,使用单例的类 SimpleDateFormat 在多线程环境中处理日期极易出现转换错误的情况。
甚至由于竞争导致解析逻辑的数字处理部分冲突,控制台照成了错误输出 。
2.解决方法1
第一种解决办法的原理是满足竞争,创建多个类 SimpleDateFormat 的实例。
package org.example.SimpleDataFormat;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class formatOK1 {static class DateTools{public static Date parse(String formatPattern, String dateString) throws ParseException {return new SimpleDateFormat(formatPattern).parse(dateString);}public static String format(String formatPattern, Date date) {return new SimpleDateFormat(formatPattern).format(date);}}static class MyThread extends Thread {private SimpleDateFormat sdf;private String dateString;public MyThread(SimpleDateFormat sdf, String dateString) {this.sdf = sdf;this.dateString = dateString;}@Overridepublic void run() {try {Date dateRef = DateTools.parse("yyyy-MM-dd",dateString);String newDataString = DateTools.format("yyyy-MM-dd",dateRef);if (!newDataString.equals(dateString)) {System.out.println("ThreadName=" + this.getName()+ "报错了 日期字符串:" + dateString + " 转换成的日期为:"+ newDataString);}} catch (ParseException e) {e.printStackTrace();}}}public static void main(String[] args) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");String[] dateStringArray = new String[]{"2000-01-01","2000-01-02","2000-01-03","2000-01-04","2000-01-05","2000-01-06","2000-01-07","2000-01-08","2000-01-09","2000-01-10",};MyThread[] threads = new MyThread[10];for (int i = 0; i < 10; i++) {threads[i] = new MyThread(sdf,dateStringArray[i]);}for (int i = 0; i < 10; i++) {threads[i].start();}}
}
运行结果如图:
控制台没有异常信息输出。
3.解决方法2
还有一种更简单的方法,那就是使用 ThreadLocal 包装SimpleDateFormat。ThreadLocal 可以为每个线程提供一个单独的 SimpleDateFormat 实例,能使线程绑定到指定对象。使用该类也可以解决多线程环境中类 SimpleDateFormat 处理日期时出现错误的问题。
package org.example.SimpleDataFormat;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class formatOK2 {static class DateTools {private static ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<>();public static SimpleDateFormat getSimpleDateFormat(String datePattern) {SimpleDateFormat sdf = null;sdf = t1.get();if (sdf == null) {sdf = new SimpleDateFormat(datePattern);t1.set(sdf);}return sdf;}}static class MyThread extends Thread {private SimpleDateFormat sdf;private String dateString;public MyThread(SimpleDateFormat sdf, String dateString) {this.sdf = sdf;this.dateString = dateString;}@Overridepublic void run() {try {Date dateRef = DateTools.getSimpleDateFormat("yyyy-MM-dd").parse(dateString);String newDataString = DateTools.getSimpleDateFormat("yyyy-MM-dd").format(dateRef);if (!newDataString.equals(dateString)) {System.out.println("ThreadName=" + this.getName()+ "报错了 日期字符串:" + dateString + " 转换成的日期为:"+ newDataString);}} catch (ParseException e) {e.printStackTrace();}}}public static void main(String[] args) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");String[] dateStringArray = new String[]{"2000-01-01","2000-01-02", "2000-01-03", "2000-01-04","2000-01-05", "2000-01-06", "2000-01-07","2000-01-08", "2000-01-09", "2000-01-10",};MyThread[] threads = new MyThread[10];for (int i = 0; i < 10; i++) {threads[i] = new MyThread(sdf, dateStringArray[i]);}for (int i = 0; i < 10; i++) {threads[i].start();}}
}
运行结果:
控制台没有异常信息输出,说明 ThreadLocal 解决了 SimpleDateFormat 非线程安全问题。
总结
加油!!!!
相关文章:

SimpleDataFormat 非线程安全
目录 前言 正文 1.出现异常 2.解决方法1 3.解决方法2 总结 前言 SimpleDateFormat 类是 Java 中处理日期和时间格式化和解析的类,但它并不是线程安全的。这意味着多个线程不能安全地共享一个 SimpleDateFormat 实例进行日期和时间的解析和格式化。当多个…...

SpringBoot : ch12 多模块配置YAML文件
前言 当您使用SpringBoot框架进行项目开发时,通常需要配置一些参数和属性。在实际开发中,可能需要将这些配置参数分成多个不同的YAML文件,并将它们组织到不同的模块中。这样可以方便管理和维护配置文件,并且可以避免配置文件的冲…...

TensorRT之LeNet5部署(onnx方式)
文章目录 前言LeNet-5部署1.ONNX文件导出2.TensorRT构建阶段(TensorRT模型文件)🧁创建Builder🍧创建Network🍭使用onnxparser构建网络🍬优化网络🍡序列化模型🍩释放资源 3.TensorRT运行时阶段(推理)&#x…...

Xilinx FPGA平台DDR3设计详解(二):DDR SDRAM组成与工作过程
本文主要介绍一下DDR SDRAM的基本组成以及工作过程,方便大家更好的理解和掌握DDR的控制与读写。 一、DDR SDRAM的基本组成 1、SDRAM的基本单元 SDRAM的基本单元是一个CMOS晶体管和一个电容组成的电路。 晶体管最上面的一端,称作栅极,通过…...
ios(swiftui) 属性包装器详解
目录 1. State 2. Binding 3. ObservedObject 和Published 4. StateObject 5. EnvironmentObject和Environment 6. AppStorage 在 SwiftUI 中,属性包装器用于增强和管理视图的状态,以及处理视图与数据模型之间的绑定和交互。下面是一些常见…...
【智能家居】面向对象编程OOP和设计模式(工厂模式)
面向对象编程 类和对象 面向对象编程和面向过程编程区别 设计模式 软件设计模式按类型分 工厂模式 面向对象编程 面向对象编程(Object-Oriented Programming,OOP)是一种程序设计范式,其中程序被组织成对象的集合,每…...
Docker安装Memcached+Python调用
简介:Memcached是一个通用的分布式内存缓存系统。它通常用于通过在RAM中缓存数据和对象来加速动态数据库驱动的网站,以减少必须读取外部数据源(如数据库或API)的次数。Memcached的API提供了一个分布在多台机器上的非常大的哈希表。…...
网页开发 HTML
目录 HTML概述 HTML结构 HTML标签语法 基本标签 标题标签 换行标签 段落标签 文本格式化标签 特殊符号 div和span标签 超链接标签 锚点 img标签 列表标签 表格标签 表单标签 HTML概述 HTML,即超文本标记语言(HyperText Markup Language …...

SHAP(五):使用 XGBoost 进行人口普查收入分类
SHAP(五):使用 XGBoost 进行人口普查收入分类 本笔记本演示了如何使用 XGBoost 预测个人年收入超过 5 万美元的概率。 它使用标准 UCI 成人收入数据集。 要下载此笔记本的副本,请访问 github。 XGBoost 等梯度增强机方法对于具有…...
LeetCode 8 字符串转整数
题目描述 字符串转换整数 (atoi) 请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C 中的 atoi 函数)。 函数 myAtoi(string s) 的算法如下: 读入字符串并丢弃无用的前导空格检查下一…...

前缀和 LeetCode1423. 可获得的最大点数
几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints 给出。 每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k 张卡牌。 你的点数就是你拿到手中的所有卡牌的点数之和。 给你一个整数数组 cardPoi…...

探索意义的深度:自然语言处理中的语义相似性
一、说明 语义相似度,反应出计算机对相同内容,不同表达的识别能力。因而识别范围至少是个句子,最大范围就是文章,其研究方法有所区别。本文将按照目前高手的研究成绩,作为谈资介绍给诸位。 二、语义相似度简介 自然语言…...
WT2605-24SS高品质录音语音芯片:实时输出、不保存本地,引领音频技术新潮流
随着科技的快速发展,高品质音频技术成为了现代社会不可或缺的一部分。在这个追求高品质、高效率的时代,唯创知音推出的WT2605-24SS高品质录音芯片,以其独特的功能和卓越的性能,引领着音频技术的新潮流。 首先,WT2605-…...
Git 合并冲突解决步骤
Git 合并冲突解决步骤 1. 找到并打开冲突文件 定位到发生冲突的文件。可以通过 Git 的命令行输出找到这些文件。例如: pom.xmlsrc/main/java/com/zzm/config/SecurityConfig.javasrc/main/java/com/zzm/service/chat/UserConversationsServiceImpl.javasrc/main/…...

Windows核心编程 注册表
目录 注册表概述 打开关闭注册表 创建删除子健 查询写入删除键值 子健和键值的枚举 常用注册表操作 注册表概述 注册表是Windows操作系统、硬件设备以及客户应用程序得以正常运行和保存设置的核心"数据库",也可以说是一个非常巨大的树状分层结构的…...

【算法专题】二分查找
二分查找 二分查找1. 二分查找2. 在排序数组中查找元素的第一和最后一个位置3. 搜索插入位置4. x 的平方根5. 山脉数组的峰顶索引6. 寻找峰值7. 寻找旋转排序数组中的最小值8. 点名 二分查找 1. 二分查找 题目链接 -> Leetcode -704.二分查找 Leetcode -704.二分查找 题…...

中国消费电子行业发展趋势及消费者需求洞察|徐礼昭
一、引言 近年来,随着科技的飞速发展,消费电子行业面临着前所未有的挑战与机遇。本文将从行业发展趋势、消费者需求洞察以及企业数字化转型的方向和动作三个方面,对消费电子行业进行深入剖析。 二、消费电子行业发展趋势 5G技术的普及和应…...

UE学习C++(1)创建actor
创建新C类 在 虚幻编辑器 中,点击 文件(File) 下拉菜单,然后选择 新建C类...(New C Class...) 命令: 此时将显示 选择父类(Choose Parent Class) 菜单。可以选择要扩展的…...
【CTA认证】Android8实现android6以下的应用运行时也要申请权限
需求 CTA入网认证,要求低版本比如Android6以下的应用,运行时,也需要有运行时权限(Runtime Permission)功能,不能默认就取到权限,必须人工在设置中打开才可。 环境 Android 8 实现 frameworks 修改思路是所有APP都…...

gRPC Java、Go、PHP使用例子
文章目录 1、Protocol Buffers定义接口1.1、编写接口服务1.2、Protobuf基础数据类型 2、服务器端实现2.1、生成gRPC服务类2.2、Java服务器端实现 3、java、go、php客户端实现3.1、Java客户端实现3.2、Go客户端实现3.3、PHP客户端实现 本文例子是在Window平台测试,Ja…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
「全栈技术解析」推客小程序系统开发:从架构设计到裂变增长的完整解决方案
在移动互联网营销竞争白热化的当下,推客小程序系统凭借其裂变传播、精准营销等特性,成为企业抢占市场的利器。本文将深度解析推客小程序系统开发的核心技术与实现路径,助力开发者打造具有市场竞争力的营销工具。 一、系统核心功能架构&…...

实战三:开发网页端界面完成黑白视频转为彩色视频
一、需求描述 设计一个简单的视频上色应用,用户可以通过网页界面上传黑白视频,系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观,不需要了解技术细节。 效果图 二、实现思路 总体思路: 用户通过Gradio界面上…...

FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...