当前位置: 首页 > 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…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

UE5 学习系列(三)创建和移动物体

这篇博客是该系列的第三篇&#xff0c;是在之前两篇博客的基础上展开&#xff0c;主要介绍如何在操作界面中创建和拖动物体&#xff0c;这篇博客跟随的视频链接如下&#xff1a; B 站视频&#xff1a;s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

基于TurtleBot3在Gazebo地图实现机器人远程控制

1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...