一、背景
在项目中,很多的地方使用到了SimpleDateFormat,用于解析时间非常的方便。日常开发过程测试,通常单元测试,或者QA测试,基本上都是单线程的,难以覆盖到多线程的测试场景;由于很多公司的并发度并不高,那么长时间也没有出现问题,这个问题比较简单又容易被大家忽略。
二、原理
SimpleDateFormat多线程不安全的核心原因,是内部实现用了calendar对象进行赋值,对于同一个SimpleDateFormat在多线程场景下进行赋值,就会相互覆盖,但是程序又能正常运行。
三、代码讲解
CountDownLatch是什么?具体介绍见文档xxxx;这里核心是线程阻塞,让线程同时运行。
这里外层用了for循环创建10个线程,创建即进入就绪状态,通过CountDownLatch控制底层多线程同时运行。测试结果发现每个线程后边经过SimpleDateFormat进行格式转化的结果已经不一致了。
四、解决方案
一种方案是每个线程使用时创建自己的SimpleDateFormat,另一种方式是使用ThreadLocal字段,让每个线程有自己的副本。这里推荐第二种方式。
五、周边
1、对象创建的几种方式
2、强弱软引用的区别
代码附录如下:
package com.zll.test;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
/**
* 验证SimpleDateFormat的非线程安全性
*/
public class SimpleDateFormatTest {
public static final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public void testSingleThread() {
for (int i = 0; i < 10; i++) {
Date date = new Date();
System.out.println(Thread.currentThread().getName() + "\t\t start");
System.out.println(Thread.currentThread().getName() + "\t\t" + date);
for (int j = 0; j < 2; j++) {
System.out.println(Thread.currentThread().getName() + "\t\t" + df.format(date));
}
System.out.println(Thread.currentThread().getName() + "\t\t end\n\n");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void testMultiThread() {
final CountDownLatch latch = new CountDownLatch(1);
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Date date = new Date();
System.out.println(Thread.currentThread().getName() + "\t\t" + date);
for (int i = 0; i < 2; i++) {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println(Thread.currentThread().getName() + "\t\t" + df.format(date));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
latch.countDown();
}
public static void main(String args[]) {
new SimpleDateFormatTest().testSingleThread();
// new SimpleDateFormatTest().testMultiThread();
}
}