为什么阿里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 」欢迎关注和转载,保留摘要,谢谢! 目录 父子类变量名相同会咋样? 为啥强制子类.父类变量名不同? ...
随机推荐
- maven搭建父子项目
父工程:父工程又称为父控制器,只是一个简单的工程,不能单独运行.作用是将子模块跟子工程聚合在一起.父控制器中的pom.xml配置,在子模块跟子工程中都可以被继承. 子工程:项目中创建的具有业务逻辑并且 ...
- 用python计算一条射线到两个平面的交点
前两天,一个朋友找我(半个程序猿)用python帮他写数学模型,当时的我直接是懵逼的,当听到三维啥的时候,整个人就好了,最终在周末花了3个小时把逻辑理了一遍,给小伙伴一个满意的答复了,话不多说,我来整 ...
- 国内免费可用的STUN服务器(webrtc 必备)
更新于2017年8月,本人亲测,国内可用,而且速度非常快! webRTC不可缺少的环节. 免费 STUN服务器列表(亲测有效)NO. STUN服务器 端口 有效 测试日期1 stun.xten.com ...
- python学习HTML之CSS(2)
1.边框的属性设置 PS:边框的高度和宽度可以采用百分比,但是高度方向的百分比基本无用,因为基数没定,参考没意义!! 2.内边距和外边距 3.在右下角添加一个“回顶部”的标签. <div> ...
- Wordpress-微信机器人高级版
微信机器人高级版是我爱水煮鱼开发的一款插件,功能很棒,运行此插件需要同时开启WPJAM Basic插件. 高级版5.0 版本对服务器要求非常高,只支持 Linux 服务器,PHP 要求 7.2 及以上 ...
- jQuery结合CSS实现手风琴组件
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- Android 短信模块分析(七) MMS数据库定义及结构整理
一. mmssms.db 数据库mmssms.db中表的定义见表4.1至4.18所示: 表4.1 addr(彩信地址) 字段名 类型 描述 备注 _id INTEGER PRIMARY_KEY 主键I ...
- Tomcat热部署与热加载!
所谓的热部署与热加载就是两个值:(reloadable='true'与autoDeloy='true')
- 关于MQTT连接的属性
连接相关的属性. 这些属性是MQTT的连接报文中连接标志字, 包含一些用于指定 MQTT 连接行为的参数. 1.清理会话(Clean Session) 客户端和服务端可以保存会话状态,以支持跨网络连接 ...
- [追热点]学习Rust之选择IDE
学习语言非常需要实际上手写代码,自然绕不开IDE工具,所以第一时间当然是选择IDE. Rust官网推荐 先去看看Rust官网推荐了什么IDE:工具 - Rust 程序设计语言 无论您喜欢用命令行还是可 ...