文档章节

SimpleDateFormat 的使用注意点(线程安全问题)

凯文加内特
 凯文加内特
发布于 2016/05/18 09:45
字数 746
阅读 372
收藏 3

Bug: Call to method of static Java.text.DateFormat
Pattern id: STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE, type: STCAL, category: MT_CORRECTNESS

As the JavaDoc states, DateFormats are inherently unsafe for multithreaded use. The detector has found a call to an instance of DateFormat that has been obtained via a static field. This looks suspicous.

For more information on this see Sun Bug #6231579 and Sun Bug #6178997.

 

    上面的英文解释其实应该说得比较清楚,在Java文档中,已经明确说明了DateFormats 是非线程安全的,而在SimpleDateFormat的Jdk 的Source文件中,我们也找到这么一段注释,说明它不是线程安全的。

Date formats are not synchronized.
* It is recommended to create separate format instances for each thread.
* If multiple threads access a format concurrently, it must be synchronized

在Sun自己的网站上。在sun的bug database中,Sun Bug #6231579 ,Sun Bug #6178997都可以印证这个问题。

    导致SimpleDateFormat出现多线程安全问题的原因,是因为:SimpleDateFormat处理复杂,Jdk的实现中使用了成员变量来传递参数,这就造成在多线程的时候会出现错误。

    而Findbugs所说的“Call to static DateFormat”,其实就是一些人:为了渐少new 的次数而把SimpleDateFormat做成成员或者静态成员,上面已经说了,这样做是不安全的。

    其实,出现这种问题的代码一般都长得差不多,典型的代码示例如下:

public class Test {
	private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

	public void method1() {
		dateFormat.format(new Date());
	}

	public void method2() {
		dateFormat.format(new Date());
	}
}

再给个详细例子说明问题,看下面代码:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class Test {
	private SimpleDateFormat dateFormat;

	public static void main(String[] args) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
		Date today = new Date();
		Date tomorrow = new Date(today.getTime() + 1000 * 60 * 60 * 24);
		System.out.println(today); // 今天是2010-01-11
		System.out.println(tomorrow); // 明天是2010-01-11
		Thread thread1 = new Thread(new Thread1(dateFormat, today));
		thread1.start();
		Thread thread2 = new Thread(new Thread2(dateFormat, tomorrow));
		thread2.start();
	}
}

class Thread1 implements Runnable {
	private SimpleDateFormat dateFormat;
	private Date date;

	public Thread1(SimpleDateFormat dateFormat, Date date) {
		this.dateFormat = dateFormat;
		this.date = date;
	}

	public void run() {
		for (;;) {// 一直循环到出问题为止吧。
			String strDate = dateFormat.format(date);
			// 如果不等于2010-01-11,证明出现线程安全问题了!!!!
			if (!"2010-01-11".equals(strDate)) {
				System.err.println("today=" + strDate);
				System.exit(0);
			}
		}
	}
}

class Thread2 implements Runnable {
	private SimpleDateFormat dateFormat;
	private Date date;

	public Thread2(SimpleDateFormat dateFormat, Date date) {
		this.dateFormat = dateFormat;
		this.date = date;
	}

	public void run() {
		for (;;) {
			String strDate = dateFormat.format(date);
			if (!"2010-01-12".equals(strDate)) {
				System.err.println("tomorrow=" + strDate);
				System.exit(0);
			}
		}
	}
}

运行的结果如下:

Mon Jan 11 11:30:36 CST 2010
Tue Jan 12 11:30:36 CST 2010
tomorrow=2010-01-11

终于看到问题了,tomorrow=2010-01-11,错得很明显了。其实要避免这个问题方法很简单,不使用SimpleDateFormat,或者不使用成员变量/静态成员变量的SimpleDateFormat对象即可。

以上出自: http://www.cnblogs.com/hyddd/articles/1643978.html

解决办法:使用org.apache.commons.lang.time.DateFormatUtils,示例:

    private final static String YYYY = "yyyy";
	private final static String sdfDay = "yyyy-MM-dd";
	private final static String sdfDays = "yyyyMMdd";
	private final static String sdfTime = "yyyy-MM-dd HH:mm:ss";

	/**
	 * 获取YYYY格式
	 * 
	 * @return
	 */
	public static String getYear() {
		return DateFormatUtils.format(new Date(), YYYY);
	}

	/**
	 * 获取YYYY-MM-DD格式
	 * 
	 * @return
	 */
	public static synchronized String getDay() {
		return DateFormatUtils.format(new Date(), sdfDay);
	}

	/**
	 * 获取YYYYMMDD格式
	 * 
	 * @return
	 */
	public static synchronized String getDays() {
		return DateFormatUtils.format(new Date(), sdfDays);
	}

	/**
	 * 获取YYYY-MM-DD HH:mm:ss格式
	 * 
	 * @return
	 */
	public static synchronized String getTime() {
		return DateFormatUtils.format(new Date(), sdfTime);
	}

 

© 著作权归作者所有

凯文加内特
粉丝 341
博文 698
码字总数 110557
作品 0
青岛
后端工程师
私信 提问
Spring单例与线程安全小结

一、Spring单例模式与线程安全 Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方。 单例模式的意思就是只有一个实例。单例模式确...

勇敢的蜗牛_Z
2016/04/07
86
0
Spring单例与线程安全小结

一、Spring单例模式与线程安全 Spring框架里的bean,或者说组件,获取实例的时候都是默认的单例模式,这是在多线程开发的时候要尤其注意的地方。 单例模式的意思就是只有一个实例。单例模式确...

java梦想家01
2016/02/18
82
2
你真的会使用SimpleDateFormat吗?

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

HollisChuang's Blog
2018/11/25
0
0
(四)Java并发学习笔记--线程不安全类与写法

常见线程不安全的类有哪些呢 下图中,我们只画出了最常见的几种情况,我们常见的Collections集合都是线程不安全的 StringBuilder-demo: 我测试的时候输出为,4985(因为线程不安全,所以每次...

潘天涯
2018/08/14
0
0
SimpleDateFormat的线程安全问题

SimpleDateFormat在进行日期格式转换时用的很多,但是 DateFormat 和 SimpleDateFormat 类不都是线程安全的,在多线程环境下调用 format() 和 parse() 方法应该使用同步代码来避免问题 * <p...

十二缸帕萨特
2015/10/16
392
0

没有更多内容

加载失败,请刷新页面

加载更多

Leetcode PHP题解--D88 696. Count Binary Substrings

D88 696. Count Binary Substrings 题目链接 696. Count Binary Substrings 题目分析 给定一个01字符串,返回仅用连续的0和1串所能组成的二进制字符串个数。 例如,00110011,就包含0011,0...

skys215
今天
2
0
基础工具类

package com.atguigu.util;import java.sql.Connection;import java.sql.SQLException;import java.util.Properties;import javax.sql.DataSource;import com.alibaba.druid......

architect刘源源
今天
49
0
P30 Pro劲敌!DxO官宣新机:排行榜又要变

5月26日晚间,DxOMark官方推特预告,将在5月27日公布一款新机型的DxOMark评分,猜猜是哪款? 网友猜想的机型有:红米K20、谷歌Pixel 3a、索尼Xperia 1、诺基亚9 PureView等。 DxOMark即将公布...

linux-tao
昨天
16
0
Ubuntu18.04.2窗口过小不能自适应(二次转载)

解决Ubuntu在虚拟机窗口不能自适应 2018年09月06日 16:20:08 起不了名儿 阅读数 855 此博文转载:https://blog.csdn.net/nuddlle/article/details/77994080(原地址) 试了很多办法这个好用 ...

tahiti_aa
昨天
2
0
死磕 java同步系列之CountDownLatch源码解析

问题 (1)CountDownLatch是什么? (2)CountDownLatch具有哪些特性? (3)CountDownLatch通常运用在什么场景中? (4)CountDownLatch的初始次数是否可以调整? 简介 CountDownLatch,可以...

彤哥读源码
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部