在日常工作的时候,编写了一个计算工作日的工具类,当时直接定义了static fina的一个SimpDateFormat,一开始没有感到哪里不对,后来想到这个时间的格式化类是线程不安全的。
SimpDateFormat线程不安全的原因:
calendar变量共享,在进行format等操作的时候,会对该对象赋值和操作,当多条线程同时访问的时候就出现了安全问题。当然要解决这个问题可以使用多种方式,可以使用java8中的LocalDateTime、LocalDate、LocalTime等时间类,或者使用ThreadLocal来实现。
我这里考虑到可能项目可能会使用java7,所以使用了ThreadLocal来做线程安全问题。所以先来说一下ThreadLocal。
1.原理
ThreadLocal是线程独享的变量,在不同的线程中有不同的副本。因为每个Thread内都存在自己的实例吗,所以Thread之间不共享该变量。ThreadLocal的实例副本通常会在线程结束时回收(注:在GC的时候如果没有外部强引用的话容易造成内存泄漏),
ThreadLocal内部实现了一个ThreadLocalMap静态内部类来储存变量,所有的get,set,remove等方法都是在操作该静态内部类的方法。
2.使用
使用的话比较简单,按自己需要决定是否实现初始化方法,然后通过get,set方法来实现数据的获取和赋值。例如:
当然,在最后使用完成以后,记得调用remove方法,清空当前线程的数据。
3.问题
ThreadLocalMap中的Key为ThreadLocal变量的弱引用(关于弱引用和强引用可以百度了解下),代码如下图:
key为ThreadLocal的弱引用,在gc的时候如果没有外部的强引用就会被回收,但是value为私有对象(强引用),就会造成value指向的对象无法回收,进而内存泄漏。
如果私用了线程池(线程会复用),那么如果数据没有remove的话,会造成数据的重复使用(脏数据)。
总的来说,出现以上问题都可以使用remove来解决,当然脏数据问题也可以在线程开始前调用set方法来设置初始值。