现象
由于SimpleDateFormat的线程安全问题,导致使用将其定义为成员变量时,会导致
public class SimpleDateFormatTest extends Thread{
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
private String name;
private String dateStr;
public SimpleDateFormatTest(String name, String dateStr) {
this.name = name;
this.dateStr = dateStr;
}
@Override
public void run() {
try {
Date date = sdf.parse(dateStr);
System.out.println(name + " --- date --- " + date);
} catch (ParseException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.execute(new SimpleDateFormatTest("A1", "20200301"));
executorService.execute(new SimpleDateFormatTest("A2", "20200325"));
executorService.execute(new SimpleDateFormatTest("A3", "20200327"));
executorService.shutdown();
}
}
原因
查看源码发现,SimpleDateFormat类内部有一个Calendar对象引用,它用来储存和这个SimpleDateFormat相关的日期信息,例如sdf.parse(dateStr),sdf.format(date) 诸如此类的方法参数传入的日期相关String,Date等,都是交由Calendar引用来储存的。这样就会导致一个问题,如果你的SimpleDateFormat是个static的, 那么多个thread之间就会共享这个SimpleDateFormat, 同时也是共享这个Calendar引用。单例、多线程、又有成员变量(这个变量在方法中是可以修改的),这个场景是不是很像servlet,在高并发的情况下,容易出现幻读成员变量的现象,故说SimpleDateFormat是线程不安全的对象。
解决方案
1、将SimpleDateFormat定义成局部变量。
缺点:每调用一次方法就会创建一个SimpleDateFormat对象,方法结束又要作为垃圾回收。
2、方法加同步锁synchronized,在同一时刻,只有一个线程可以执行类中的某个方法。
缺点:性能较差,每次都要等待锁释放后其他线程才能进入。
3、使用java8提供的DateTimeFormatter。
0条评论