其实SimpleDateFormat线程非安全这个问题,自己一直都是清楚的,这个清楚局限在大家都这么说或者相关的技术文档也是这么说,但是对于线程非安全的细节一直都是不清楚,所以乘着这个机会,了解下其中的相关实现细节。
SimpleDateFormat线程非安全,说到底就是多线程环境下,SimpleDateFormat这个类中存在一个多线程的共享变量/资源,在多线程场景下,每个线程都需要对这个变量进行操作,如果此变量未被锁定,那在多线程环境下,就会引发线程安全问题。顺着这个思路,我们去探究SimpleDateFormat这个类,首先查看这个类的类继承结构,如下:

查看其继承的父类DateFormat的类成员变量信息:

注意其中有一个Calendar类型的变量名calendar,其中的注释信息描述为:

大体的意思就是:这个Calendar实例对象是用来计算data-time的字段并且用于格式化和时间转化,子类需要初始化calendar对象。由于这个类并没有使用volatile或者juc中的AtomicReference进行处理。并且目前场景中只会使用parse()方法以及format()方法,继续查看这两个方法的相关定义,如下所示,并没有使用synchronize关键字进行修饰或者有相关代码块:


下面我们来细致分析下多线程环境下使用,会有什么样的影响,先从parse方法进行分析:

查看calb这个变量的类型CalendarBuilder,通过名称就能直接判断出这是个用于产生Calendar实例对象的构造者,这个establish方法就是用于构建一个Calendar实例,具体方法如下:

其中在建立Calendar对象时,有三步相关的更新操作,如上述截图中所示,在多线程环境下,如果其中一个线程进行了第一/第二操作,同时另外的线程执行了clear操作,会将设置的Calendar对象清除,发生未知异常。format方法中问题类似,这里不过多介绍。
解决办法也有很多,其中jdk8中提供的新包java.time中的时间接口都是immutable and thread-safe(线程安全的),也有比如apache common的FastDateFormat等都是线程安全的实现。
上面是自己针对的SimpleDateFormat类非线程安全的理解,可能还有不足之处,如有问题,希望不吝指出,多谢。
网友评论