为什么阿里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 」欢迎关注和转载,保留摘要,谢谢! 目录 父子类变量名相同会咋样? 为啥强制子类.父类变量名不同? ...
随机推荐
- 更改yii框架入口文件位置,修改前后端访问路径
将frontend/web/index.php复制到项目根目录,修改为: <?php defined('YII_DEBUG') or define('YII_DEBUG', true); def ...
- 【PAT甲级】1100 Mars Numbers (20 分)
题意: 输入一个正整数N(<100),接着输入N组数据每组包括一行字符串,将其翻译为另一个星球的数字. AAAAAccepted code: #define HAVE_STRUCT_TIMESP ...
- 记录我对'我们有成熟的时间复杂度为O(n)的算法得到数组中任意第k大的数'的误解
这篇博客记录我对剑指offer第2版"面试题39:数组中出现次数超过一半的数字"题解1的一句话的一个小误解,以及汇总一下涉及partition算法的相关题目. 在剑指offer第2 ...
- python opencv:保存图像
- Doc-Compose
一.安装sudo curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s ...
- 免费的私人代码托管(bitbucket) 和 常用git指令
转自 http://blog.csdn.net/nzing/article/details/24452475 今天想找个免费的私人代码托管平台,github,googlecode, SourceFor ...
- JS获取光标在input 或 texterea 中下标位置
<textarea placeholder="请输入表达式" id="methodInput" ></textarea> 获取位置: v ...
- Mysql安装 ----> 基于源码包安装
1)基于源码包安装MySQL [root@localhost ~]# rpm -q mysql mysql-server mariadb mairadb-server //检查有没 ...
- Python 爬取 热词并进行分类数据分析-[解释修复+热词引用]
日期:2020.02.02 博客期:141 星期日 [本博客的代码如若要使用,请在下方评论区留言,之后再用(就是跟我说一声)] 所有相关跳转: a.[简单准备] b.[云图制作+数据导入] c.[拓扑 ...
- ha-wordy-Write-up
信息收集 下载地址:点我 bilibili:点我 ➜ ~ nmap -sn 192.168.116.1/24 Starting Nmap 7.80 ( https://nmap.org ) at 20 ...