1.多线程环境下SimpleDateFormat的不安全问题:
SimpleDateFormat的format方法实际操作的就是Calendar(Calendar变量也就是一个共享变量线程不安全)。
也正是因为每次在转化时间的时候foramat会先把时间set到calendar中,这样就会导致A线程读取到B线程的时间
image
image
我们来试一下:
定义两个全局常量
private static final String myDateStr = "2022-01-01";
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
//写一个测试方法:
private static void test(Callable task) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Future future = pool.submit(task);
list.add(future);
}
for (Future future : list) {
System.out.println(future.get());
}
pool.shutdown();
}
//开始测试:
public static void main(String[] args) throws Exception {
test(()->dateFormat.parse(myDateStr));
}
果然控制台报错!
image
解决办法:
1.每次使用parse直接new一个SimpleDateFormat
public static void main(String[] args) throws Exception {
test(()->new SimpleDateFormat("yyyy-MM-dd").parse(myDateStr));
}
打印正常!
2.使用synchronized同步锁
public static void main(String[] args) throws Exception {
test(()->{ synchronized(dateFormat) { return dateFormat.parse(myDateStr);} });
}
打印正常!
3.使用TreadLocal
private static final ThreadLocal<SimpleDateFormat> df = ThreadLocal.withInitial(() ->new SimpleDateFormat("yyyy-MM-dd"));
public static Date parseToDate(String dateString) {
try {
return df.get().parse(dateString);
} catch (ParseException e) {
return null;
}
}
public static void main(String[] args) throws Exception {
test(()->parseToDate(myDateStr));
}
打印正常!
4.使用java8 DateTimeFormatter线程安全对象
public static void main(String[] args) throws Exception {
test(()->LocalDate.parse(myDateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd")));
}
打印正常!