当前位置: 首页 > news >正文

深入理解高并发编程 - SimpleDateFormat 类的线程安全问题

1、重现与解决

1.1、重现

import java.text.SimpleDateFormat;
import java.util.Date;public class UnsafeSimpleDateFormatExample {public static void main(String[] args) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Runnable task = () -> {for (int i = 0; i < 5; i++) {String formattedDate = sdf.format(new Date());System.out.println(Thread.currentThread().getName() + ": Formatted Date: " + formattedDate);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}};Thread thread1 = new Thread(task, "Thread-1");Thread thread2 = new Thread(task, "Thread-2");thread1.start();thread2.start();}
}

在这个示例中,创建了两个线程,它们共享同一个 SimpleDateFormat 实例来格式化当前日期和时间。由于 SimpleDateFormat 内部状态不是线程安全的,可能会观察到以下问题:

输出的日期格式可能交错出现,因为两个线程在竞争访问共享的 SimpleDateFormat 实例,导致格式化结果混乱。
可能会抛出异常,如 ArrayIndexOutOfBoundsException 或 StringIndexOutOfBoundsException,这是因为线程竞争导致 SimpleDateFormat 内部状态不一致。

这个示例中,每个线程都会尝试格式化当前日期和时间,并在输出时稍微延迟一段时间。由于没有对 SimpleDateFormat 进行线程保护,两个线程可能会相互干扰,导致输出的格式化结果不符合预期,甚至引发异常。

要避免这个问题,可以使用线程局部变量(ThreadLocal)来确保每个线程都有自己的 SimpleDateFormat 实例,或者使用线程安全的替代类,如 Java 8 中的 DateTimeFormatter。

1.2、使用线程局部变量(ThreadLocal)

package com.lfsun.main.basic.myjuc.depthstudy.threadsafequestion;import java.text.SimpleDateFormat;
import java.util.Date;public class ThreadLocalSimpleDateFormatExample {private static ThreadLocal<SimpleDateFormat> threadLocalSdf = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));public static void main(String[] args) {Runnable task = () -> {SimpleDateFormat sdf = threadLocalSdf.get();for (int i = 0; i < 5; i++) {String formattedDate = sdf.format(new Date());System.out.println(Thread.currentThread().getName() + ": Formatted Date: " + formattedDate);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}};Thread thread1 = new Thread(task, "Thread-1");Thread thread2 = new Thread(task, "Thread-2");thread1.start();thread2.start();}
}

使用 ThreadLocal 的主要目的是为每个线程提供一个独立的实例,从而避免共享资源的线程安全问题。不需要额外的同步机制,除非在某些特定情况下需要跨线程共享某些资源。

1.3、使用线程安全的替代类,如 Java 8 中的 DateTimeFormatter。

package com.lfsun.main.basic.myjuc.depthstudy.threadsafequestion;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;public class DateTimeFormatterExample {public static void main(String[] args) {DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");Runnable task = () -> {for (int i = 0; i < 5; i++) {String formattedDate = dtf.format(LocalDateTime.now());System.out.println(Thread.currentThread().getName() + ": Formatted Date: " + formattedDate);try {Thread.sleep(100); // 模拟一些处理时间} catch (InterruptedException e) {e.printStackTrace();}}};// 创建和启动多个线程以演示线程安全Thread thread1 = new Thread(task, "Thread-1");Thread thread2 = new Thread(task, "Thread-2");thread1.start();thread2.start();}
}

1.4、Joda-Time

在 Java 8 引入新的 java.time 包之前,Joda-Time被广泛用于处理日期和时间。Joda-Time 提供了一组线程安全的日期和时间类,可以帮助解决 SimpleDateFormat 在多线程环境中的线程安全问题。

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;public class JodaTimeExample {public static void main(String[] args) {DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");Runnable task = () -> {for (int i = 0; i < 5; i++) {String formattedDate = dtf.print(DateTime.now());System.out.println(Thread.currentThread().getName() + ": Formatted Date: " + formattedDate);try {Thread.sleep(100); // 模拟一些处理时间} catch (InterruptedException e) {e.printStackTrace();}}};// 创建和启动多个线程以演示线程安全Thread thread1 = new Thread(task, "Thread-1");Thread thread2 = new Thread(task, "Thread-2");thread1.start();thread2.start();}
}

在这个示例中,使用了 Joda-Time 库的 DateTime 类和 DateTimeFormatter 来格式化和解析日期。Joda-Time 提供的日期和时间类都是线程安全的,因此可以在多线程环境中共享使用,无需担心线程安全问题。

需要注意的是,从 Java 8 开始,Java 标准库中引入了新的日期和时间 API(java.time 包),其中包含了线程安全的日期和时间类。所以,在 Java 8 及以后的版本中,也可以使用这个新的日期和时间 API 来避免 SimpleDateFormat 的线程安全问题。

2、SimpleDateFormat 类为何不是线程安全的?

SimpleDateFormat 类不是线程安全的主要原因在于其内部状态以及对共享资源的操作可能会导致竞争条件和数据不一致。以下是一些导致 SimpleDateFormat 类不是线程安全的原因:

内部状态共享: SimpleDateFormat 内部维护了日期格式化模式(pattern)、时区信息、语言环境等状态。多个线程同时使用同一个 SimpleDateFormat 实例时,它们会访问和修改相同的内部状态,可能导致数据混乱。线程间竞争: 在多线程环境中,多个线程同时对同一个 SimpleDateFormat 实例进行格式化或解析操作时,它们会竞争修改内部状态,导致输出结果混乱。非原子性操作: SimpleDateFormat 内部的状态修改操作可能不是原子性的,这意味着多个线程在同时修改状态时可能会产生竞争条件,导致数据错误或异常。共享的 Calendar 实例: SimpleDateFormat 内部使用了共享的 Calendar 实例,可能会在多线程环境中产生问题,因为 Calendar 本身也不是线程安全的。

由于上述原因,如果多个线程同时使用同一个 SimpleDateFormat 实例进行格式化和解析操作,就有可能导致线程不安全的问题,从而产生意外的结果、异常或混乱的输出。

相关文章:

深入理解高并发编程 - SimpleDateFormat 类的线程安全问题

1、重现与解决 1.1、重现 import java.text.SimpleDateFormat; import java.util.Date;public class UnsafeSimpleDateFormatExample {public static void main(String[] args) {SimpleDateFormat sdf new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Runnable task…...

接口幂等性实现方式

优质博文&#xff1a;IT-BLOG-CN 幂等 操作的特点是一次和多次请求某一个资源对于资源本身应该具有同样的结果&#xff08;网络超时等问题除外&#xff09;。幂等函数或幂等方法是指可以使用相同参数重复执行&#xff0c;并能获得相同结果的函数。这些函数不会影响系统状态&am…...

redis高可用之持久化

目录 一、Redis 高可用的相关知识 1&#xff09;什么是高可用 2&#xff09;Redis的高可用技术 3&#xff09;持久化的功能 4&#xff09;redis持久化的方式 二、RDB持久化 1&#xff09;RDB持久化的触发方式 &#xff08;1&#xff09;手动触发 &#xff08;2&…...

Cocos Creator 3.8 后期效果 Shader 编写(2/2) 进阶篇

前言 在上一篇文章中&#xff0c;麒麟子给大家分享了如何在 Cocos Creator 3.8 中的自定义管线中&#xff0c;添加属于自己的后期效果 Shader。 但基于 BlitScreen 的方案&#xff0c;我们只能编写最简单后效 Shader&#xff0c;如果我们想要支持更多复杂的 Shader&#xff0c…...

【JS自用模板】自动点击选课的操作模板

以激动点击课程为案例复习一下基本前端&#xff0c;容易涉及的问题包括如何提取object类的数字&#xff0c;setTimeout为什么不起作用&#xff1f; 具体思路是&#xff0c;此处会立刻选中符合条件的页面元素打开&#xff0c;然后1小时后会刷新页面&#xff0c;相应地播放页面也…...

TENNECO EDI 项目——X12与XML之间的转换

近期为了帮助广大用户更好地使用 EDI 系统&#xff0c;我们根据以往的项目实施经验&#xff0c;将成熟的 EDI 项目进行开源。用户安装好知行之桥EDI系统之后&#xff0c;只需要下载我们整理好的示例代码&#xff0c;并放置在知行之桥指定的工作区中&#xff0c;即可开始使用。 …...

C++项目:在线五子棋对战(网页版)

项目介绍 本项⽬主要实现⼀个⽹⻚版的五⼦棋对战游戏&#xff0c;其主要⽀持以下核⼼功能&#xff1a; • 用户管理:实现用户注册&#xff0c;用户登录、获取用户信息、用户天梯分数记录、用户比赛场次记录等。 • 匹配对战:实现两个玩家在网页端根据天梯分数匹配游戏对⼿&…...

flutter遇到的小问题记录

flutter-getx的Get.bottomSheet组件改变高度 Get.bottomSheet( isScrollControlled: true,) isScrollControlled: true 就是控制高度 (无语) 截取视频第一针 返回的是本地url 或者Uint8List的数据 String? videoStr await VideoThumbnail.thumbnailFile(video: videoPath,…...

Golang bitset 基本使用

安装&#xff1a; go get github.com/bits-and-blooms/bitset下面代码把fmtx换成fmt就行 //------------基本操作------------//构建一个64bit长度的bitsetb : bitset.New(64)//放入一个数b.Set(10)fmtx.Println("add-10&#xff1a;", b.DumpAsBits()) // 0000000…...

sql 分组讨论,二级分组(非2个字段分组),使用 窗口函数和普通分组实现

1. 二级分组需求 先按照一个字段分组&#xff0c;在按照 第二个字段分组。之后&#xff0c;如果 这个 二级分组中的数据&#xff0c;是 > 1条的。就筛选出来。 比如&#xff1a; 先按照 站点分组&#xff0c;再按照 设备分组&#xff0c; 即&#xff1a;如果站点上配置了…...

业务中如何过滤敏感词

在我们访问网站的时候&#xff0c;如果发现我们发布的内容有色情暴力的东西等等&#xff0c;会屏蔽掉&#xff0c;这种行为就是过滤敏感词。 从技术层面实现起来&#xff0c;其实比较简单&#xff0c;因为我们输入的内容就是一个大型的字符串&#xff0c;我们要调用某些api来判…...

用服务器搭建网站需要做什么

网站建设是一个广义的术语&#xff0c;涵盖了许多不同的技能和学科中所使用的生产和维护的网站。不同领域的网页设计&#xff0c;网页图形设计&#xff0c;界面设计&#xff0c;创作&#xff0c;其中包括标准化的代码和专有软件&#xff0c;用户体验设计和搜索引擎优化。许多人…...

clickhouse 删除操作

OLAP 数据库设计的宗旨在于分析适合一次插入多次查询的业务场景&#xff0c;市面上成熟的 AP 数据库在更新和删除操作上支持的均不是很好&#xff0c;当然 clickhouse 也不例外。但是不友好不代表不支持&#xff0c;本文主要介绍在 clickhouse 中如何实现数据的删除&#xff0c…...

C 语言中,「.」与「->」有什么区别?

使用“.”的话&#xff0c;只需要声明一个结构体。格式是结构体类型名结构体名。然后通过结构体名加上“.”再加上域名&#xff0c;就可以引用结构体的域了。因为结构体的内存是自动分配的&#xff0c;就像使用int a;一样。而使用“->”的话&#xff0c;需要声明一个结构体的…...

github pages 用法详解 发布自己的网站

github pages 基础用法 URL 规则 假设你的 github 帐号为 mygithub&#xff0c;需要发布的仓库名为 myrepo&#xff0c;那么 pages 的 URL 为&#xff1a; https://mygithub.github.io/myrepo 添加内容 用任意编辑器写好&#xff08;或者生成&#xff09;标准的网页内容&a…...

坤简炫酷的JQuery轮播图插件

介绍&#xff1a; 找到了一个炫酷的JQuery轮播图插件&#xff0c;只需要配置三四行代码就可以实现很多二维三维炫酷的切换效果。 视频效果及教程&#xff1a; https://www.bilibili.com/video/BV1Fu4y1d776/ 代码&#xff1a; https://github.com/w-x-x-w/AwesomeWeb 使用…...

C# 条件编译

C# 条件编译 C# 条件编译&#xff1a;根据不同的需求&#xff0c;编译生成不同的程序版本&#xff0c;条件编译是一种编译预处理命令&#xff0c;它是在编译代码之前对源代码进行处理。它可以根据条件&#xff0c;决定是否编译某段代码 条件编译的三种形式&#xff1a; 第一种…...

IntelliJ IDEA如何重新弹出git身份验证窗口

1、点击File菜单—>点击Settings—>点击Appearance & Behavior—>点击System Settings—>点击Passwords—>选中Do not save, forget passwords after restart—>点击Apply—>点击OK&#xff0c;如下所示&#xff1a; 2、重启IntelliJ IDEA—>通过g…...

【雕爷学编程】Arduino动手做(200)---WS2812B幻彩LED灯带4

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…...

【雕爷学编程】Arduino动手做(201)---DFRobot 行空板03

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#x…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…...