为什么阿里Java规约要求谨慎使用SimpleDateFormat
前言
在阿里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的更多相关文章
- 为什么阿里Java规约要求谨慎修改serialVersionUID字段
serialVersionUID简要介绍 serialVersionUID是在Java序列化.反序列化对象时起作用的一个字段.Java的序列化机制是通过判断类的serialVersionUID来验证版 ...
- 为什么阿里Java规约禁止使用Java内置线程池?
IDEA导入阿里规约插件,当你这样写代码时,插件就会自动监测出来,并给你红线提醒. 告诉你手动创建线程池,效果会更好. 在探秘原因之前我们要先了解一下线程池 ThreadPoolExecutor 都有 ...
- 点评阿里JAVA手册之编程规约(OOP 规约 、集合处理 、并发处理 、其他)
下载原版阿里JAVA开发手册 [阿里巴巴Java开发手册v1.2.0] 本文主要是对照阿里开发手册,注释自己在工作中运用情况. 本文难度系数为三星(★★★) 本文为第二篇 第一篇 点评阿里JAVA手 ...
- 点评阿里JAVA手册之异常日志(异常处理 日志规约 )
下载原版阿里JAVA开发手册 [阿里巴巴Java开发手册v1.2.0] 本文主要是对照阿里开发手册,注释自己在工作中运用情况. 本文内容:异常处理 日志规约 本文难度系数为一星(★) 本文为第三篇 ...
- 点评阿里JAVA手册之编程规约(命名风格、常量定义、代码风格、控制语句、注释规约)
下载原版阿里JAVA开发手册 [阿里巴巴Java开发手册v1.2.0] 本文主要是对照阿里开发手册,注释自己在工作中运用情况. 本文难度系数为一星(★) 码出高效.码出质量. 代码的字里行间流淌的是 ...
- 点评阿里JAVA手册之MySQL数据库 (建表规约、索引规约、SQL语句、ORM映射)
下载原版阿里JAVA开发手册 [阿里巴巴Java开发手册v1.2.0] 本文主要是对照阿里开发手册,注释自己在工作中运用情况. 本文内容:MySQL数据库 (建表规约.索引规约.SQL语句.ORM映 ...
- 阅读阿里Java开发手册记录
概述 在阅读完阿里Java开发手册(嵩山版)后,发现自己在开发过程中有一些没有按照规范开发的情况,这里将容易忘记的规范记录下来,并且添加自己的理解,一方面方便自己巩固记忆,另一方面希望对其他同学能够提 ...
- 阿里巴巴Java规约插件试用
阿里Java开发规约Eclipse插件介绍 阿里巴巴集团配合<阿里巴巴Java开发手册>PDF终极版开发的IDE插件,目前包括IDEA插件.Eclipse插件. 安装 检查环境 插件要求: ...
- 阿里 Java 手册系列教程:为啥强制子类、父类变量名不同?
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 目录 父子类变量名相同会咋样? 为啥强制子类.父类变量名不同? ...
随机推荐
- 移动端,ajax 动态加载的元素,为动态添加的一系列同个类名的元素添加点击事件
背景:一个列表页,有一系列同类名的元素,需要为每一个动态添加的列表项添加事件: 点击选择下图中不同的文档类型,再通过 ajax 动态加载不同的文档. 使用过的方法: 1.通知 jquery 的 $(s ...
- ubuntu apache 通过端口新建多个站点
cd /etc/apache2/sites-available 最近的虚拟机没绑定域名,所以呢,就先用域名加端口新建几个站点用着 1. vim /etc/apapche2/apapche2.conf ...
- ENTRYPOINT与CMD/实现切换用户执行
1.CMD 最终会被解析成:["cmd","arg1","arg2"] 可以在运行时被覆盖 2.ENTRYPOINT 最终解析成 [&quo ...
- 17 JavaScript Cookies
关于Cookie: Cookie是存储在电脑上的文本文件中的一些数据 Cookie致力于解决如何在连接关闭后记录客户单的用户信息 Cookie以键值对的形式存储,例如username=John Doe ...
- Nexus-VDC(Virtual Device Context)
VDC实际上是将一台物理的Nexus7K设备虚拟为多个逻辑的VDC设备,该术语叫做VDC(Virtual Device Context),该虚拟技术实际上是在一个物理设备架构和内核上运行多个VDC,已 ...
- ElasticSearch学习记录 - 命令示例
GET /searchfilmcomments/searchfilmcomments/_search { "query": { "match_all": {} ...
- from flyai.dataset import Dataset 报错
from flyai.dataset import Dataset 报错 No module name 'flyai' 先找到ide中使用的Python对应的pip的位置. windows用户 ...
- c++ 读取、输出txt文件
下面这段话转自:https://blog.csdn.net/lightlater/article/details/6326338 关于文本文件的文件头 第一 ANSI文件的文件头为空,不需要处理: 第 ...
- 自定义 directive pagination
学习angular过程中,对directive 不是很了解,很有必要自己写一个,以便知道它的各方面的处理方式. directive 中 scope 与 controller 交互,有三种定义策略 &q ...
- 使用外网访问Flask项目
在学习flask过程中,想使用手机访问项目,根据flask手册中可以将 app.run(host='192.168.1.109', port=8000,debug=True) 但是发现手机依然无法连接 ...