为什么SimpleDateFormat不是线程安全的?以及解决方法说明

一:概述

SimpleDateFormat 类主要负责日期的转换与格式化等操作,在多线程的环境中,使用此类容易造成数据转换及处理的不正确,因为 SimpleDateFormat 类并不是线程安全的,但在单线程环境下是没有问题的。

SimpleDateFormat 在类注释中也提醒大家不适用于多线程场景:

 

 

说的很清楚,SimpleDateFormat 不是线程安全的,多线程下需要为每个线程创建不同的实例。

不安全的原因是因为使用了 Calendar 这个全局变量:

 

在日期格式化的时候:

 

 

二:线程不安全的原因

我们把重点放在 calendar ,这个 format 方法在执行过程中,会操作成员变量 calendar 来保存时间 calendar.setTime(date) 。

但由于在声明 SimpleDateFormat 的时候,使用的是 static 定义的,那么这个 SimpleDateFormat 就是一个共享变量,SimpleDateFormat 中的 calendar 也就可以被多个线程访问到,所以问题就出现了,举个例子:

假设线程 A 刚执行完 calendar.setTime(date) 语句,把时间设置为 2020-09-01,但线程还没执行完,线程 B 又执行了 calendar.setTime(date) 语句,把时间设置为 2020-09-02,这个时候就出现幻读了,线程 A 继续执行下去的时候,拿到的 calendar.getTime 得到的时间就是线程B改过之后的。

除了 format() 方法以外,SimpleDateFormat 的 parse 方法也有同样的问题。

至此,我们发现了 SimpleDateFormat 的弊端,所以为了解决这个问题就是不要把 SimpleDateFormat 当做一个共享变量来使用

 

阿里巴巴 java 开发规范是怎么描述 SimpleDateFormat 的:

 

 

 

三、模拟线程安全问题

无码无真相,接下来我们创建一个线程来模拟 SimpleDateFormat 线程安全问题:

创建 TestThread.java 类:

 

结果:

从控制台打印的结果来看,使用单例的 SimpleDateFormat 类在多线程的环境中处理日期转换,极易出现转换异常(java.lang.NumberFormatException:multiple points)以及转换错误的情况。

代码:

public class TestThread extends Thread {private SimpleDateFormat simpleDateFormat;// 要转换的日期字符串private String dateString;public TestThread(SimpleDateFormat simpleDateFormat, String dateString) {this.simpleDateFormat = simpleDateFormat;this.dateString = dateString;}@Overridepublic void run() {try {Date date = simpleDateFormat.parse(dateString);String newDate = simpleDateFormat.format(date).toString();if (!newDate.equals(dateString)) {System.out.println("ThreadName=" + this.getName()+ " 报错了,日期字符串:" + dateString+ " 转换成的日期为:" + newDate);}} catch (ParseException e) {e.printStackTrace();}}
}// 一般我们使用SimpleDateFormat的时候会把它定义为一个静态变量,避免频繁创建它的对象实例private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY-MM-dd");public static void main(String[] args) {String[] dateStringArray = new String[]{"2021-03-10", "2021-05-1", "2021-05-19", "2021-09-17"};TestThread[] threads = new TestThread[4];// 创建线程for (int i = 0; i < 4; i++) {threads[i] = new TestThread(simpleDateFormat, dateStringArray[i]);}// 启动线程for (int i = 0; i < 4; i++) {threads[i].start();}}

 

 

五、如何解决线程安全

1、每次使用就创建一个新的 SimpleDateFormat

创建全局工具类 DateUtils.java

 

public class DateUtils {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);}
}

 

 2:加synchronized 锁

简单粗暴,synchronized 往上一套也可以解决线程安全问题,缺点自然就是并发量大的时候会对性能有影响,因为使用了 synchronized 加锁后的多线程就相当于串行,线程阻塞,执行效率低

public class DateUtilsSynchronized {private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static Date parse(String formatPattern, String dateString) throws ParseException {synchronized (simpleDateFormat){return simpleDateFormat.parse(dateString);}}public static String format(String formatPattern, Date date) {synchronized (simpleDateFormat){return simpleDateFormat.format(date);}}
}

 

3、使用ThreadLocal

ThreadLocal 是 java 里一种特殊的变量,ThreadLocal 提供了线程本地的实例,它与普通变量的区别在于,每个使用该线程变量的线程都会初始化一个完全独立的实例副本。

hreadLocal 可以确保每个线程都可以得到单独的一个 SimpleDateFormat 的对象,那么就不会存在竞争问题。

 

public class DateUtilsThreadLocal {private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {@Overrideprotected DateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd");}};public static Date parse(String formatPattern, String dateString) throws ParseException {return threadLocal.get().parse(dateString);}public static String format(String formatPattern, Date date) {return threadLocal.get().format(date);}
}

4:推荐写法 

上边提到的阿里巴巴 java 开发手册给出了说明:如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。

日期转换,SimpleDateFormat 固然好用,但是现在我们已经有了更好地选择,Java 8 引入了新的日期时间 API,并引入了线程安全的日期类,一起来看看。

Instant:瞬时实例。
LocalDate:本地日期,不包含具体时间 例如:2014-01-14 可以用来记录生日、纪念日、加盟日等。
LocalTime:本地时间,不包含日期。
LocalDateTime:组合了日期和时间,但不包含时差和时区信息。
ZonedDateTime:最完整的日期时间,包含时区和相对UTC或格林威治的时差。
新API还引入了 ZoneOffSet 和 ZoneId 类,使得解决时区问题更为简便。

解析、格式化时间的 DateTimeFormatter 类也进行了全部重新设计。

例如,我们使用 LocalDate 代替 Date,使用 DateTimeFormatter 代替 SimpleDateFormat,如下所示:

 String DateNow = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"));System.out.println(DateNow);

这样就避免了 SimpleDateFormat 的线程不安全问题啦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/269539.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java+random+oracle,oracle随机数 — dbms_random

oracle随机数 — dbms_randomORACLE的PL/SQL提供了生成随机数和随机字符串的多种方式&#xff0c;罗列如下:1、小数( 0 ~ 1)select dbms_random.value from dual2、指定范围内的小数 ( 0 ~ 100 )select dbms_random.value(0,100) from dual3、指定范围内的整数 ( 0 ~ 100 )sele…

《Effective C#》读书笔记-1.C# 语言习惯-2.使用运行时常量(readonly)而不是编译时常量(const)...

概念 编译时编译时顾名思义就是正在编译的时候。那啥叫编译呢&#xff1f;就是编译器帮你把源代码翻译成机器能识别的代码。(当然只是一般意义上这么说&#xff0c;实际上可能只是翻译成某个中间状态的语言。比如Java只有JVM识别的字节码&#xff0c;C#中只有CLR能识别的MSIL。…

MQ介绍

一&#xff1a;MQ介绍 1:异步 2:解耦 3:削峰 二&#xff1a;MQ优缺点 三&#xff1a;Kafka和RabbitMQ和RocketMQ比较

白月光

张信哲----白月光http://mu.njzx.com/gt/nan/zxz-200409/01.mp3白月光 心里某个地方那么亮 却那么冰凉每个人都有一段悲伤想隐藏却欲盖弥彰白月光 照天涯的两端在心上 却不在身旁擦不干你当时的泪光路太长追不回原谅你是我不能言说的伤想遗忘又忍不住回想像流亡一路跌跌撞撞…

oracle 次月,Oracle日期查询:季度、月份、星期等时间信息

摘要&#xff1a;Select to_char(sysdate,Q) from dual;--指定日期的季度Select to_char(sysdate,MM) from dual;--月份Select to_char(sysdate,WW) from dual;--当年第几周Select to_char(sysdate,Q) from dual;--指定日期的季度Select to_char(sysdate,MM) from dual;--月份S…

转:A/B测试:实现方法

概念&#xff1a;http://www.aliued.cn/2010/09/13/ab-testing-basic-concept.html 我们先来看一个图&#xff1a; &#xff08;注&#xff1a;感谢Algo提供本图。&#xff09; 上图展示了 A/B 测试的实现原理。从左到右&#xff0c;四条较粗的竖线代表了 A/B 测试中的四个关键…

同学录

在中学同学的提醒下&#xff0c;去5460和ChinaRen的同学录更新了我的联系方式等等&#xff0c;了解到中学同学过年时在家乡搞了一个聚会。很久没有见过他们了&#xff0c;最近的一次是去年在zte南研所见到张辉&#xff0c;他从zte深圳来南京出差&#xff0c;此后不久我就离开了…

linux命令编写四位数密码本,grub-crypt命令 – 对口令进行加密

grub-crypt命令的作用是对口令进行加密。grub-crypt命令支持对口令进行MD5和SHA加密&#xff0c;默认情况下是对口令进行SHA-512加密。SHA是Secure Hash Algorithm的简写&#xff0c;中文为安全散列算法&#xff0c;是美国国家安全局 设计&#xff0c;美国国家标准与技术研究院…

mac修改host

1.打开终端 2.输入sudo vi /etc/hosts 3.输入密码 4.进入文件hosts&#xff0c;然后按“i”&#xff0c;进入编辑模式 5.把你的host添加到最后 6.esc推出编辑模式 7.输入:wq&#xff0c;保存退出

宝洁广告的智慧

有趣的是&#xff0c;一大批写文章针砭宝洁拿稿费的营销高手&#xff0c;同时也在满心期待某天能加入宝洁一了夙愿。而宝洁的形象之所以如此深入人心&#xff0c;除了家家户户都在用宝洁公司的产品之外&#xff0c;更离不开十余年宝洁公司通过“宁拙勿巧、气势绵长”的广告运作…

语法之知识点的改进(Func/Action)

上一章我们讲到关于面向对象思想上C#和JAVA之差别。笔者分别从面向对象的三大特性入手。而本章主要讲一些C#改进的知识点。在.NET Framework 2.0之后出现很多新的知识点。这些知识点更是让C#在写法上更加的多样性。有些写法还真的让笔者觉得很有不错。由于这一部分的知识更多是…

linux取字符串的前面,Linux Shell 截取字符串

shell中截取字符串的方法很多${var#*/}${var##*/}${var%/*}${var%%/*}${var:start:len}${var:start}${var:0-start:len}${var:0-start}下面用几个例子展示一下&#xff1a;1) 获得字符串的长度语法&#xff1a;${#var}示例代码&#xff1a;str"http://www.fengbohello.xin…

vs.net 2005 beta 2安装问题

我之前已经卸载了BETA 1了&#xff0c;但可惜安装后&#xff0c;在新建工程后&#xff0c;还是出现 "package load failur"等错误信息。比较郁闷。于是搜索了下&#xff0c;发现有位外国牛人的blog,提供了很好的解决方法 http://blogs.msdn.com/astebner/default.asp…

idea修改新的git提交地址

更换git地址步骤 1、点击VCS 2、点击Git 3、点击Remotes 点击框中链接即可在右边看到一个铅笔字样的按钮&#xff0c;即可看到如图所示弹窗 点击铅笔&#xff08;编辑&#xff09;&#xff0c;看到新的弹窗&#xff0c;链接已经被默认选中这时候粘贴新的git地址&#xff0c;点…

[HNOI2008]玩具装箱toy(dp+斜率优化)

斜率优化问题一般都是决策单调问题。对于这题能够证明单调决策。 令sum[i]sigma(c [k] ) 1<k<i , f[i]sum[i]i , cL1; 首先我们能够写出转移方程 dp[i] min( dp[j] (f[i]-f[j]-c)^2 ) 。令决策j1<j2。若决策j2更优有 dp[j2](f[i]-f[j2]-c)^2<dp[j1](f[i]-f[…

全球500强企业人力资源管理之道

美国杜邦的组织机构改革 美国杜邦公司(DuPont)是世界上最大的化学公司&#xff0c;建立至今&#xff0c;已近200年。杜邦公司所创设的组织机构&#xff0c;曾成为美国各公司包括著名大公司的模式&#xff0c;并反映了企业组织机构发展演变的一般特点。 拜耳公司人力资源管理…

mac idea实现全局替换

点击Edit ----- Find ----- Replace in Path 例如要把项目中的cc替换为aa 点击Replace All

linux下防火墙脚本,Linux系统如何修改防火墙配置

这篇文章主要介绍了Linux系统如何修改防火墙配置,文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下改Linux系统防火墙配置需要修改 /etc/sysconfig/iptables 这个文件vim /etc/sysconfig/iptables在vim编辑器&…

现代希腊语字母表

转载于:https://www.cnblogs.com/zhangzujin/p/6782532.html