前言

在阿里Java开发规约中,有强制性的提到SimpleDateFormat 是线程不安全的类 ,在使用的时候应当注意线程安全问题,如下:

其实之前已经介绍过使用JDK1.8的DateTimeFormatter 和LocalDateTime来处理时间了,还在用SimpleDateFormat?Java8都发布N年了,转LocalDateTime吧。今天,就来说说SimpleDateFormat的线程安全问题。

SimpleDateFormat是非线程安全的

时间处理,基本所有项目上都是需要使用到的,往往很多初学者会把SimpleDateFormat定义为static类型,然后在进行时间转化的时候没有做加锁处理。如下:

public class Main {private final static SimpleDateFormat SDFT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static void main(String[] args) throws ParseException {System.out.println(SDFT.parse("2019-05-29 12:12:12"));}}

当然,本代码直接运行是没有问题的。但是,当此SDFT实例应用到多线程环境下的时候,就会出现致命的问题。假设有如下代码:

public class Main {private static SimpleDateFormat SDFT = new SimpleDateFormat("yyyy-MM-dd");public static void main(String[] args) {for (int i = 1; i < 31; i++) {int ii = i;new Thread(() -> {Date date = null;try {String s = "2019-05-" + ii;date = SDFT.parse(s);System.out.println("" + ii + ":" + date.getDate());} catch (ParseException e) {e.printStackTrace();}}).start();}}}

此代码的意思是创建30个线程,去转化不同的时间字符串,然后做打印输出,运行结果:

(运行此代码也有可能出现由线程安全问题引起的异常)

根据“预期结果”,两边的数字应该是相等的,为何这里输出不相等呢?通过DateFormat源码可以查看:

因为SimpleDateFormat定义为了共享的,所以其类里的属性calendar也是多个线程共享的,这就造成了线程安全问题。

解决方案

方案一:加锁处理

如本文例子,可以通过加锁来保证线程安全:

public class Main {private static SimpleDateFormat SDFT = new SimpleDateFormat("yyyy-MM-dd");public static void main(String[] args) {for (int i = 1; i < 31; i++) {int ii = i;new Thread(() -> {Date date = null;try {String s = "2019-05-" + ii;synchronized (Main.class) {date = SDFT.parse(s);}System.out.println("" + ii + ":" + date.getDate());} catch (ParseException e) {e.printStackTrace();}}).start();}}}

输出:

4:4
3:3
1:1
2:2
29:29
28:28
27:27
26:26
30:30
25:25
23:23
21:21
20:20
22:22
18:18
24:24
19:19
17:17
16:16
14:14
15:15
12:12
13:13
10:10
11:11
9:9
7:7
6:6
5:5
8:8

方案二:每次都创建SimpleDateFormat实例

代码改造如下:

    public static void main(String[] args) {for (int i = 1; i < 31; i++) {int ii = i;new Thread(() -> {Date date = null;try {String s = "2019-05-" + ii;date = new SimpleDateFormat("yyyy-MM-dd").parse(s);System.out.println("" + ii + ":" + date.getDate());} catch (ParseException e) {e.printStackTrace();}}).start();}}

每次使用SimpleDateFormat的时候,都去创建一个SimpleDateFormat实例,保证SimpleDateFormat实例不被共享。

方案三:使用LocalThread

这是阿里Java规约里提到的解决方法之一,之所以可以使用LocalThread来解决此问题,代码改造如下:

public class Main {private static final ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {@Overrideprotected SimpleDateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd");}};public static void main(String[] args) {for (int i = 1; i < 31; i++) {int ii = i;new Thread(() -> {Date date = null;try {String s = "2019-05-" + ii;date = threadLocal.get().parse(s);System.out.println("" + ii + ":" + date.getDate());} catch (ParseException e) {e.printStackTrace();}}).start();}}}

运行结果如下:

22:22
2:2
24:24
15:15
17:17
16:16
29:29
9:9
30:30
3:3
4:4
5:5
12:12
8:8
20:20
26:26
21:21
28:28
19:19
27:27
18:18
1:1
14:14
25:25
11:11
13:13
7:7
6:6
23:23
10:10

解决方法四:使用JDK1.8提供的DateTimeFormatter来处理时间,这里就不赘述了,可以参考我之前的文章。

转载于:https://www.cnblogs.com/happy4java/p/11206096.html

为什么阿里Java规约要求谨慎使用SimpleDateFormat相关推荐

  1. serialversionuid的作用_为什么阿里Java规约要求谨慎修改serialVersionUID字段

    serialVersionUID简要介绍 serialVersionUID是在Java序列化.反序列化对象时起作用的一个字段.Java的序列化机制是通过判断类的serialVersionUID来验证版 ...

  2. 为什么阿里Java规约禁止使用Java内置Executors创建线程池?

    IDEA导入阿里规约插件,当你这样写代码时,插件就会自动监测出来,并给你红线提醒. 告诉你手动创建线程池,效果会更好. 在探秘原因之前我们要先了解一下线程池 ThreadPoolExecutor 都有 ...

  3. 阿里Java开发手册之编程规约

    阿里Java开发手册之编程规约 对于程序员来说,编程规范可以养成良好的编程习惯,提高代码质量,降低沟通成本.就在2月9号,阿里出了一份Java开发手册(正式版),分为编程规约,异常日志,MySQL规约 ...

  4. 9月26日云栖精选夜读:阿里Java代码规约插件即将全球首发,邀您来发布仪式现场...

    10月14日,杭州云栖大会研发效能峰会场,阿里将正式对外开放Java代码规约插件及源码. 热点热议 阿里Java代码规约插件即将全球首发,邀您来发布仪式现场 作者:云效平台   发表在:阿里云效平台 ...

  5. java代码内创建mysql索引_点评阿里JAVA手册之MySQL数据库 (建表规约、索引规约、SQL语句、ORM映射)...

    下载原版阿里JAVA开发手册  [阿里巴巴Java开发手册v1.2.0] 本文主要是对照阿里开发手册,注释自己在工作中运用情况. 本文内容:MySQL数据库 (建表规约.索引规约.SQL语句.ORM映 ...

  6. 阿里 java 开发手册

    关闭 学习中 <a href="https://climberclimbing.github.io/">Climbercliming </a> 目录视图 摘 ...

  7. 阿里java规范手册总结

    本文总结一些常见编码注意问题,每条规范来自各个模块,所以序号有点凌乱,一些简单及很少用的规范没有进行收录,具体的规范可见文末阿里java规范.pdf,进行下载详细阅读 OOP规约 7.[强制]所有的相 ...

  8. java m4a文件拼接_面试官:为啥不提倡字符串拼接?看阿里java开发手册怎么说

    阿里规约 先来看阿里java开发手册的第22条,循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展. 说明:下例中,反编译出的字节码文件显示每次循环都会 ne ...

  9. 关于java变量命名,介绍阿里JAVA命名规范及IDEA实时检测插件的使用以及CODELF取名神器

    JAVA开发中,我们经常会为如何来命名烦心. 有大神曾说计算机科学的两件难事:缓存失效和命名 的确,程序员最头疼的事:命名 但其实,有规范可以遵循.还有工具帮我们取名,更有插件帮我们实时检查是否违反规 ...

最新文章

  1. linux系统预定义变量有哪些,Shell预定义变量用法详解
  2. win8+XAML Binding(数据绑定)
  3. 不停歇的 Java 即将发布 JDK 16,新特性速览!
  4. 朴素贝叶斯分类器(Navie Bayesian Classifier)中的几个要点(一)
  5. CSS_03_04_CSS伪元素选择器
  6. python进阶八_警告和异常
  7. GoJS v1.8.27 去水印方法
  8. 他不怕被拒绝_不怕被拒绝,这几大星座男追人时最有耐心
  9. cuda编程_CUDA编程入门(四)并行归约算法
  10. vue 父组件与子组件之间的传值(主动传值)
  11. 老李分享:基于图像识别的跨平台的手机自动化测试方案
  12. HDU 2639(01背包求第K大值)
  13. linux管理员基础知识
  14. 【经验分享】强力推荐——截图小工具Faststone Capture(FSC)
  15. 2020-09-21
  16. mac桌面键盘快捷键_使用键盘快捷键更改桌面分辨率
  17. IDEA使用database时,连接MySQL后schemas不显示数据库名的情况
  18. [UE4][Blueprint]虚幻4实现照相机拍照功能
  19. 纯css实现手风琴效果_创建纯CSS手风琴的4种方法
  20. 关于thinkpad安装win10操作系统

热门文章

  1. python spark2.0_Python+Spark2.0+hadoop学习笔记——Python Spark MLlib决策树二分类
  2. 安装centos7步骤_Centos7下源码编译安装mysql5.7 详细步骤 小白也能安装
  3. python getattr_深入浅出Python模块
  4. WebPack基础入门(一):万物皆可webpack
  5. mysql i o error_警告:mysqli_ERROR()需要精确的一个参数,0给定的错误
  6. python3导入模块原理_python模块导入原理
  7. MySQL数据类型中的二进制类型有_在MySQL数据类型中,常用的数据类型有()。A.()数值类型()B.()字符串类型()C.()日期时间类型()D.()二进制类型...
  8. 初中三年级计算机课程教案,三年级信息技术教案下
  9. 计算机专业经典书籍强烈推荐
  10. 值得收藏的Redis基础总结