文档章节

Java 线程不安全的SimpleDateFormat

晨猫
 晨猫
发布于 2018/01/31 20:13
字数 824
阅读 31
收藏 5

SimpleDateFormat是Java提供的一个格式化和解析日期的工具类
但是由于它是线程不安全的,多线程共用一个SimpleDateFormat实例对日期进行解析或者格式化会导致程序出错

问题重现

public class TestSimpleDateFormat {
    //(1)创建单例实例
    static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static void main(String[] args) {
        //(2)创建多个线程,并启动
        for (int i = 0; i <100 ; ++i) {
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    try {//(3)使用单例日期实例解析文本
                        System.out.println(sdf.parse("2017-12-13 15:17:27"));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();//(4)启动线程
        }
    }
}

代码(1)创建了SimpleDateFormat的一个实例,
代码(2)创建100个线程,每个线程都公用同一个sdf对象对文本日期进行解析,
多运行几次就会抛出java.lang.NumberFormatException异常,
加大线程的个数有利于该问题复现。


问题分析

SimpleDateFormat实例里面有一个Calendar对象
SimpleDateFormat之所以是线程不安全的就是因为Calendar是线程不安全的
而Calendar之所以是线程不安全的是因为其中存放日期数据的变量都是线程不安全的,比如里面的fields,time等

解决方案

第一种方式:每次使用时候new一个SimpleDateFormat的实例,这样可以保证每个实例使用自己的Calendar实例,但是每次使用都需要new一个对象,并且使用后由于没有其它引用,就会需要被回收,开销会很大。
第二种方式:可以使用synchronized进行同步,但使用同步意味着多个线程要竞争锁,在高并发场景下会导致系统响应性能下降。


Thread thread = new Thread(new Runnable() {
    public void run() {
        try {// (3)使用单例日期实例解析文本
            synchronized (sdf) {
                System.out.println(sdf.parse("2017-12-13 15:17:27"));
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
});


第三种方式:使用ThreadLocal,这样每个线程只需要使用一个SimpleDateFormat实例相比第一种方式大大节省了对象的创建销毁开销,并且不需要对多个线程直接进行同步,使用ThreadLocal方式代码如下:


public class TestSimpleDateFormat2 {
    // (1)创建threadlocal实例
    static ThreadLocal<DateFormat> safeSdf = new ThreadLocal<DateFormat>(){
        @Override 
        protected SimpleDateFormat initialValue(){
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };
    
    public static void main(String[] args) {
        // (2)创建多个线程,并启动
        for (int i = 0; i < 10; ++i) {
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    try {// (3)使用单例日期实例解析文本
                            System.out.println(safeSdf.get().parse("2017-12-13 15:17:27"));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();// (4)启动线程
        }
    }
}


代码(1)创建了一个线程安全的SimpleDateFormat实例,步骤(3)在使用的时候首先使用get()方法获取当前线程下SimpleDateFormat的实例,在第一次调用ThreadLocal的get()方法适合会触发其initialValue方法用来创建当前线程所需要的SimpleDateFormat对象。

总结

SimpleDateFormat是线程不安全的,应该避免多线程下使用SimpleDateFormat的单个实例,多线程下使用时候最好使用ThreadLocal对象

转自:http://ifeve.com/notsafesimpledateformat/

本文转载自:http://ifeve.com/notsafesimpledateformat/

晨猫
粉丝 42
博文 443
码字总数 137138
作品 0
杭州
后端工程师
私信 提问
ThreadLocal趣谈 —— 杨过和他的四个冤家

一个一个上 一日醒来,杨过发现小龙女离家出走,于是外出寻找,不料碰上了金轮法王、李莫愁、裘千尺、公孙止四个冤家。 “哼,四个打我一个,算什么英雄好汉,有本事的,一个一个上!” 按照...

SexyCode
2018/06/12
0
0
你真的会使用SimpleDateFormat吗?

在日常开发中,我们经常会用到时间,我们有很多办法在Java代码中获取时间。但是不同的方法获取到的时间的格式都不尽相同,这时候就需要一种格式化工具,把时间显示成我们需要的格式。 最常用...

HollisChuang's Blog
2018/11/25
0
0
java并发编程——线程不安全的类

什么是线程不安全的类? 如果一个类的对象同时被多个线程访问,如果不做特殊的同步或并发处理,很容易表现出线程不安全的现象,比如抛出异常、逻辑处理错误等,这种类我们就称为线程不安全的...

长头发-dawn
2018/10/08
12
0
Java并发编程笔记之SimpleDateFormat源码分析

SimpleDateFormat 是 Java 提供的一个格式化和解析日期的工具类,日常开发中应该经常会用到,但是由于它是线程不安全的,多线程公用一个 SimpleDateFormat 实例对日期进行解析或者格式化会导...

狂小白
2018/07/12
0
0
Java并发编程之SimpleDateFormat源码分析

SimpleDateFormat 是 Java 提供的一个格式化和解析日期的工具类,日常开发中应该经常会用到,但是由于它是线程不安全的,多线程公用一个 SimpleDateFormat 实例对日期进行解析或者格式化会导...

狂小白
2018/07/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

高防CDN的出现是为了解决网站的哪些问题?

高防CDN是为了更好的服务网络而出现的,是通过高防DNS来实现的。高防CDN是通过智能化的系统判断来路,再反馈给用户,可以减轻用户使用过程的复杂程度。通过智能DNS解析,能让网站访问者连接到...

云漫网络Ruan
59分钟前
8
0
聊聊Tomcat中的连接器(Connector)

上期回顾 上一篇文章《Tomcat在SpringBoot中是如何启动的》从main方法启动说起,窥探了SpringBoot是如何启动Tomcat的,在分析Tomcat中我们重点提到了,Tomcat主要包括2个组件,连接器(Conne...

木木匠
今天
8
0
OSChina 周一乱弹 —— 熟悉的味道,难道这就是恋爱的感觉

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @xiaoshiyue :好久没分享歌了分享张碧晨的单曲《今后我与自己流浪》 《今后我与自己流浪》- 张碧晨 手机党少年们想听歌,请使劲儿戳(这里)...

小小编辑
今天
2.1K
21
SpringBoot中 集成 redisTemplate 对 Redis 的操作(二)

SpringBoot中 集成 redisTemplate 对 Redis 的操作(二) List 类型的操作 1、 向列表左侧添加数据 Long leftPush = redisTemplate.opsForList().leftPush("name", name); 2、 向列表右......

TcWong
今天
37
0
排序––快速排序(二)

根据排序––快速排序(一)的描述,现准备写一个快速排序的主体框架: 1、首先需要设置一个枢轴元素即setPivot(int i); 2、然后需要与枢轴元素进行比较即int comparePivot(int j); 3、最后...

FAT_mt
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部