「日常开发」记一次因使用Date引起的线上BUG处理

生活中,我们需要掌控自己的时间,减少加班,提高效率;日常开发中,我们需要操作时间API,保证效率、安全、稳定。现在都2020年了,了解如何在JDK8及以后的版本中更好地操控时间就很有必要,尤其是一次线上BUG的发生,让小明更是深有体会。

背景

Java8以前,每每操控时间,我们经常使用的类库就是Date,并且会通过SimpleDateFormat类对时间进行格式化。你可知道?Date类是一个可变类,SimpleDateFormat类也是线程不安全的,因此在多线程的场景下执行格式化操作时,就会发生意想不到的情况。下面我们看一下使用DateSimpleDateFormat在多线程下可能发生的问题以及使用LocalDateTimeDateTimeFormatter的方法和优势。

问题来了

多线程环境下,使用DateSimpleDateFormat时,如果我们将它定义为一个静态变量使用,虽然会避免重复创建实例, 但是会出现个别线程获取时间失败的现象,我们通过代码模拟这个场景:

运行main方法,查看控制台会发现有个别线程会报java.lang.NumberFormatException异常。类似下图所示:

问题分析

接下来,我们通过查看源码进一步分析(多图预警),可以看到SimpleDateFormat是直接继承的DateFormat类:

并重写了parse()(字符串转日期)和 format()(日期转字符串)方法,因此我们重点从这两个方法来分析。

首先是SimpleDateFormatparse()方法,该方法中创建了一个CalendarBuilder对象,

再往下看,会看到CalendarBuilder使用establish方法将变量calendar设值到其属性中,

![image-20200420012213545](/Users/xin/Library/Application Support/typora-user-images/image-20200420012213545.png)

calendar是父类DateFormat类的共享变量,可以被多个线程访问到

因此当SimpleDateFormat声明为static时,线程并不安全,多个线程同时操作访问就会抛出异常。

同样地通过查看format(),我们发现format方法中有一行calendar.setTime(date);也是操作的该共享变量calendar,线程也是不安全的。

有趣的是,在DateFormat源码注释上作者也已经给出醒目的提示:

使用Google翻译过来就是

日期格式不同步。 建议为每个线程创建单独的格式实例。 如果多个线程同时访问一种格式,则必须在外部同步该格式。

解决方案

小明有一句座右铭,方法总比问题多。我们来看几个小明认为不错的解决方案。

1、仅在需要用到的地方创建一个新的实例,就没有线程安全问题。

点评:加重了创建对象的负担,频繁地创建和销毁对象,消耗资源,效率较低。

2、通过synchronized解决线程安全问题;

点评:并发量大的时候会对性能有影响,容易造成线程阻塞。

3、通过ThreadLocal保证线程之间变量不共享

点评:ThreadLocal可以确保每个线程都可以得到单独的一个SimpleDateFormat的对象,那么自然也就不存在竞争问题了。就是有点大材小用。

以上就是小明能够提供的所有方案。什么,都不满意?我们来看一下2020年JDK8的解决方案。

使用LocalDateTime

Java8以后,我们有了新的选择,使用LocalDateTime时间类。首先,LocalDateTime本身是线程安全的,其对应的格式化工具类DateTimeFormatter也是线程安全的,不存在变量共享,每一个属性字段都用了final关键字修饰,因此每次操作后都是返回的copy对象。并且LocalDateTime类本身也有很多操作时间的API来替代传统的Calendar类。

基于Java8DateTimeFormatter的解决方案,我们对之前的代码进行改造,多线程环境下,运行代码,并未发现任何异常,稳定高效:

我们可以看到在DateTimeFormatter源码上作者也贴心的加注释说明,该类是不可变的,并且是线程安全的。

同理,这点我们也可以从LocalDateTime的官方源码中看出。

其他骚操作

为了让大家忘掉之前使用Calendar操作时间的笨拙,我们来切实感受一下LocalDateTime给实际开发中带来的便利:

更多举例说明,请点击文末阅读原文

代码地址:https://github.com/WhenCoding/coder-xiaoming

总结

综上,小明推荐小伙伴们使用JDK8的LocalDateTime系列来取代Date系列,这样做不仅能够保证线上项目平稳运行,而且通过其自带的API还能操作时间,还能提高开发效率,今晚可以不加班!

欢迎大家访问我的个人博客网站:https://mynamecoder.com

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容