类SimpleDateFormat主要负责日期的转换与格式化,但在多线程的情况下,使用此类容易造成数据转换错误。
用一个例子来演示其线程不安全问题:
输入日期,通过调用parse和format方法,转化为日期字符串,与原日期相比。
package p7;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyThread extends Thread{
private SimpleDateFormat sdf;
private String dateString;
public MyThread(SimpleDateFormat sdf, String dateString) {
super();
this.sdf = sdf;
this.dateString = dateString;
}
public void run() {
try {
Date dateRef=sdf.parse(dateString);
String s=sdf.format(dateRef);
System.out.println("原时间:"+dateString+" 解析时间:"+s);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package p7;
import java.text.SimpleDateFormat;
public class Test1 {
public static void main(String[] args) {
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
String []dateStringArray= {"2000-01-01","2000-01-02","1997-03-04"};
MyThread[] myThreads=new MyThread[3];
for(int i=0;i<3;i++) {
myThreads[i]=new MyThread(simpleDateFormat, dateStringArray[i]);
myThreads[i].start();
}
}
}
运行结果如下:
从结果上来看日期通过parse和format转化之后,与原日期不符合,出现数据转换不准确的问题。
下面我们就来分析一下为什么会产生此问题。
通过查看SimpleDateFormat的源代码我们可以发现,SimpleDateFormat内部声明了一个protected的Calendar类型的成员变量,而format()和parse()方法都是对该calendar进行修改。
format方法源代码如下:
因为该方法没有进行同步,当多线程访问的时候都对同一个calendar对象进行操作,导致数据错误。
假设线程1调用format方法执行完calendar.setTime(date)后停止,线程2此时也调用format()方法执行calendar.setTime(date)方法,然后线程1和线程2再继续执行后面的代码,这时候线程1和线程2改变的都是同一个calendar中的值,导致线程访问数据出现差错。
那么在多线程下要如何解决这个问题呢?
方法一:
编写DateTools类,每此调用方法都创建一个SimpleDateFormat对象。
public class DateTools {
public static Date parse(String formatPattern,String dateString) throws ParseException {
return new SimpleDateFormat(formatPattern).parse(dateString);
}
public static String format(String formatPattern,Date date) {
return new SimpleDateFormat(formatPattern).format(date);
}
}
方法二:
使用ThreadLocal类,每个线程都拥有自己的SimpleDateFormat对象
class DateTool{
private static ThreadLocal<SimpleDateFormat>t1=new ThreadLocal<>();
public static SimpleDateFormat getSimpleDateFormat(String pattern) {
SimpleDateFormat sdf=null;
sdf=t1.get();
if(sdf==null) {
sdf=new SimpleDateFormat(pattern);
t1.set(sdf);
}
return sdf;
}
}