前言

在阿里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>() {
@Override
protected 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来处理时间,这里就不赘述了,可以参考我之前的文章。

为什么阿里Java规约要求谨慎使用SimpleDateFormat的更多相关文章

  1. 为什么阿里Java规约要求谨慎修改serialVersionUID字段

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

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

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

  3. 点评阿里JAVA手册之编程规约(OOP 规约 、集合处理 、并发处理 、其他)

    下载原版阿里JAVA开发手册  [阿里巴巴Java开发手册v1.2.0] 本文主要是对照阿里开发手册,注释自己在工作中运用情况. 本文难度系数为三星(★★★) 本文为第二篇 第一篇 点评阿里JAVA手 ...

  4. 点评阿里JAVA手册之异常日志(异常处理 日志规约 )

    下载原版阿里JAVA开发手册  [阿里巴巴Java开发手册v1.2.0] 本文主要是对照阿里开发手册,注释自己在工作中运用情况. 本文内容:异常处理 日志规约 本文难度系数为一星(★) 本文为第三篇 ...

  5. 点评阿里JAVA手册之编程规约(命名风格、常量定义、代码风格、控制语句、注释规约)

    下载原版阿里JAVA开发手册  [阿里巴巴Java开发手册v1.2.0] 本文主要是对照阿里开发手册,注释自己在工作中运用情况. 本文难度系数为一星(★) 码出高效.码出质量. 代码的字里行间流淌的是 ...

  6. 点评阿里JAVA手册之MySQL数据库 (建表规约、索引规约、SQL语句、ORM映射)

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

  7. 阅读阿里Java开发手册记录

    概述 在阅读完阿里Java开发手册(嵩山版)后,发现自己在开发过程中有一些没有按照规范开发的情况,这里将容易忘记的规范记录下来,并且添加自己的理解,一方面方便自己巩固记忆,另一方面希望对其他同学能够提 ...

  8. 阿里巴巴Java规约插件试用

    阿里Java开发规约Eclipse插件介绍 阿里巴巴集团配合<阿里巴巴Java开发手册>PDF终极版开发的IDE插件,目前包括IDEA插件.Eclipse插件. 安装 检查环境 插件要求: ...

  9. 阿里 Java 手册系列教程:为啥强制子类、父类变量名不同?

    摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 父子类变量名相同会咋样? 为啥强制子类.父类变量名不同? ...

随机推荐

  1. JenKins docker 集群

    //tag 桉树有时间来搞 **阿斯蒂 啊 阿斯蒂

  2. 【MySQL】常用增删改查

    目录 1. 文件夹(库) 2. 文件(表) 3. 文件内容(数据) "@ ___ 1. 文件夹(库) # 增 create database db charset utf8; # 查 sho ...

  3. Linux虚拟机(CentOS)安装gcc, g++

    1.    确保自己的虚拟机联网 点击那个三角形可以选择连接网络 如果还是连不了网,参考https://www.cnblogs.com/xingbo/p/6100554.html 2.联网后,使用命令 ...

  4. windows ,linux永久和临时修改pip源

    临时修改(建议)pypi镜像源方法:如果有untrust 报错,可使用https开头的网站,或加上--trusted 例如: pip install pywin32 -i http://mirrors ...

  5. Net Core解决ZipFile解压中文出现乱码

    一.在main方法中添加 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 二.解压添加 //sourceArchiveFi ...

  6. Steam 游戏 《Crashlands(崩溃大陆)》修改器制作-[先使用CE写,之后有时间的话改用CheatMaker](2020年寒假小目标12)

    日期:2020.02.15 博客期:155 星期六 [温馨提示]: 只是想要修改器的网友,可以直接点击此链接下载: 只是想拿CT文件的网友,可以直接点击此链接下载: 没有博客园账号的网友,可以将页面下 ...

  7. .NET中的字符串(3):字符串的比较

    在.NET中,对字符串的比较操作并不仅仅是简单的比较二者的值,= =操作首先比较两个字符串的引用,如果引用相同,就直接返回True:如果不同再去比较它们的值.所以如果两个值相同的字符串的比较相对于引用 ...

  8. 拓扑排序板子 hihocoder-1174

    思路 不断删入度为1的点及其出边. 图解 #include <bits/stdc++.h> using namespace std; const int maxn=1e5+10; vect ...

  9. Python的基础知识,不同于其他编程语言

    1.字符串拼接可以不使用+号 name = "this " "is " "a " "string" 2.使用''' ‘’ ...

  10. netty实现websocket客户端(附:测试服务端代码)

    1,客户端启动类 package test3; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import ...