欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SimpleDateFormat 的使用注意点 博客分类: Java javaSimpleDateFormat日期格式化DateFormat 

程序员文章站 2024-03-16 16:05:10
...

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

 

但我认为还有一个办法可行,就是使用 ThreadLocal ,为每个线程保存该变量的一个副本,这样在各个线程中使用该 SimpleDateFormat 时,就不会与其他线程中的 SimpleDateFormat  变量发生冲突

上一篇: cocos2dx 3.2 事件机制

下一篇: