当前位置:网站首页>SimpleDateFormat线程安全问题和解决方案
SimpleDateFormat线程安全问题和解决方案
2022-08-09 16:17:00 【泡^泡】
为什么线程不安全
在多线程环境下,当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改,因此线程是不安全的。
public StringBuffer format(Date date, StringBuffer toAppendTo,FieldPosition pos){
pos.beginIndex = pos.endIndex = 0;
return format(date,toAppendTo,pos.getFieldDelegate());
}
// Called from Format after creating a FieldDelegate
private StringBuffer format(Date date,StringBuffer toAppendTo,FieldDelegate delegate) {
// Convert input date to time field list
calendar.setTime(date);
boolean useDateFormatSymbols = useDateFormatSymbols();
for (int i = 0; i < compiledPattern.length;) {
int tag = compiledPattern[i] >>> 8;
int count = compiledPattern[i++] & 0xff;
if (count == 255) {
count = compiledPattern[i++] << 16;
count |= compiledPattern[i++];
}
switch (tag) {
case TAG_QUOTE_ASCII_CHAR:
toAppendTo.append((char)count);
break;
case TAG_QUOTE_CHARS:
toAppendTo.append(compiledPattern, i, count);
i += count;
break;
default:
subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
break;
}
}
return toAppendTo;
}
protected Calendar calendar;
可以看到,多个线程之间共享变量calendar,并修改calendar。因此在多线程环境下,当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改,因此线程是不安全的。
此外,parse方法也是线程不安全的,parse方法实际调用的是CalenderBuilder的establish来进行解析,其方法中主要步骤不是原子操作。
解决方案
- 使用ThreadLocal,每个线程都拥有自己的SimpleDateFormat对象副本
- 加一把线程同步锁:synchronized(lock)
- 将SimpleDateFormat定义成局部变量
- 使用DateTimeFormatter代替SimpleDateFormat
,DateTimeFormatter是线程安全的,默认提供了很多格式化方法,也可以通过ofPattern方法创建自定义格式化方法。
package com;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SimpleDateFormatTest {
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal();
private static DateFormat getDateFormat(){
//从当前线程的范围内获得一个DateFormat
DateFormat dateFormat = threadLocal.get();
if(dateFormat == null){
dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//Thread.currentThread范围内设置一个SimpleDateFormat
threadLocal.set(dateFormat);
}
return dateFormat;
}
public static Date parse(String strDate) throws ParseException {
return getDateFormat().parse(strDate);
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 20;i++){
executorService.execute(()->{
try {
System.out.println(parse("2022-07-09 10:00:00"));
} catch (ParseException e) {
e.printStackTrace();
}
});
}
}
}
格式化日期示例:
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); // 2019-11-20T15:04:29.017
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String strDate=localDateTime.format(dtf);
System.out.println(strDate); // 2019/23/20 15:23:46
解析日期:
DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime localDateTime=LocalDateTime.parse("2019/11/20 15:23:46",dtf);
System.out.println(localDateTime); // 2019-11-20T15:23:46
边栏推荐
- 【.NET6+Modbus】Modbus TCP协议解析、仿真环境以及基于.NET实现基础通信
- CryoEM粒子(Particle)类型预测的数据集构建
- CocosCreator accesses WeChat mini-games
- Optimization of a piece of JDBC code (Part 1)
- Functions and Features of Smart Home Control System
- 微软 .NET Core 3.1 年底将结束支持,请升级到.NET 6
- B46 - STM32太阳能充电智能心率监测骑行仪
- MySQL 5.5系列安装步骤教程(图解版)
- WinForm(四)一种实现登录的方式
- 一键生成 API 文档的妙招
猜你喜欢
随机推荐
B024 – STM32温湿度控制体温检测烟雾报警系统
Redis的那些事:一文入门Redis的基础操作
智能工具管理系统
什么是控制板定制开发?
A48基于NRF24L01的无线心率血氧体温检测
.NET 6 study notes (4) - Solve the Nullable warning in VS2022
WPF 实现柱形统计图
JVM内存模型和结构详解(五大模型图解)
冷冻电镜聚类中心(2D Class)粒子图像的解析
B46 - STM32太阳能充电智能心率监测骑行仪
MySQL 5.5系列安装步骤教程(图解版)
利用C#传输Json数据
WinForm(四)一种实现登录的方式
什么是硬件集成开发?硬件集成开发的核心有哪些?
MySQL 5.5 series installation steps tutorial (graphical version)
【开源教程4】疯壳·开源编队无人机-OPENMV 脚本烧写
程序员的专属浪漫——用3D Engine 5分钟实现烟花绽放效果
MASA Stack 第三期社区例会
ceph2
快捷键修改typora字体----自制脚本









