②. 从阿里ThreadLocal规范开始
- ①. 公司业务:在对一些业务日志写入数据库的时候,日期调用了sdf的静态,导致了会报错或者日期乱了(生产故障)
①. 非线程安全的SimpleDateFormat
- ①. 写时间工具类,一般写成静态的成员变量,不知,此种写法的多线程下的危险性!
- ②. 代码展示
public class DateUtils { public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * 模拟并发环境下使用SimpleDateFormat的parse方法将字符串转换成Date对象 * @param stringDate * @return * @throws Exception */ public static Date parseDate(String stringDate)throws Exception { return sdf.parse(stringDate); } public static void main(String[] args) throws Exception { for (int i = 1; i <=30; i++) { new Thread(() -> { try { System.out.println(DateUtils.parseDate("2020-11-11 11:11:11")); } catch (Exception e) { e.printStackTrace(); } },String.valueOf(i)).start(); } } }
③. bug
④. 源码分析结论(了解)
SimpleDateFormat类内部有一个Calendar对象引用,它用来储存和这个
SimpleDateFormat相关的日期信息,例如sdf.parse(dateStr),sdf.format(date) 诸如此类的方法参数传入的日期相关String,Date等等, 都是交由Calendar引用来储存的.这样就会导致一个问题如果你的SimpleDateFormat是个static的, 那么多个thread 之间就会共享这个SimpleDateFormat, 同时也是共享这个Calendar引用
②. 将SimpleDateFormat定义成局部变量(方案一)
- 缺点:每调用一次方法就会创建一个SimpleDateFormat对象,方法结束又要作为垃圾回收
public class DateUtils { public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * 模拟并发环境下使用SimpleDateFormat的parse方法将字符串转换成Date对象 * @param stringDate * @return * @throws Exception */ public static Date parseDate(String stringDate)throws Exception { return sdf.parse(stringDate); } public static void main(String[] args) throws Exception { for (int i = 1; i <=30; i++) { new Thread(() -> { try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println(sdf.parse("2020-11-11 11:11:11")); sdf = null; } catch (Exception e) { e.printStackTrace(); } },String.valueOf(i)).start(); } }