出现问题的原因
多个线程的task指向了同一个SimpleDateFormat对象,SimpleDateFormat是非线程安全的。
解决问题的方案
- 方案1:加锁
格式化代码是在最后一句return dateFormat.format(date);,所以可以为最后一句代码添加synchronized锁
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT 开始计时
Date date = new Date(1000 * seconds);
String s;
synchronized (ThreadLocalNormalUsage04.class) {
s = dateFormat.format(date);
}
return s;
}
运行结果
运行结果中没有发现相同的时间,达到了线程安全的目的
缺点:因为添加了synchronized,所以会保证同一时间只有一条线程可以执行,这在高并发场景下肯定不是一个好的选择,所以看看其他方案吧。
- 方案2:使用ThreadLocal
/**
* 利用 ThreadLocal 给每个线程分配自己的 dateFormat 对象
* 不但保证了线程安全,还高效的利用了内存
*/
public class ThreadLocalNormalUsage05 {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i ) {
int finalI = i;
//提交任务
threadPool.submit(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage05().date(finalI);
System.out.println(date);
}
});
}
threadPool.shutdown();
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT 开始计时
Date date = new Date(1000 * seconds);
//获取 SimpleDateFormat 对象
SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal.get();
return dateFormat.format(date);
}
}
class ThreadSafeFormatter {
public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new
ThreadLocal<SimpleDateFormat>(){
//创建一份 SimpleDateFormat 对象
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
}
};
}
运行结果
使用了ThreadLocal后不同的线程不会有共享的 SimpleDateFormat 对象,所以也就不会有线程安全问题
2.2 实践场景2
当前用户信息需要被线程内的所有方法共享
- 方案1:传递参数