之前mark了一下忘记回复了233
因为之前跟原来项目经理聊天的时候有聊到过这个场景,特意来回复一下
针对ThreadLocal没有手动remove的情况可能会出现的问题
如果是单纯的单例情况下 确实不会有问题,因为每次启动完了spring自己就回收了,但是如果在线程池的调用情况下 就会有问题,因为线程池中的线程是固定维护的,如果你手动set了以后没有及时remove,可能导致后续的任务获取到前一个副本的值,比如这个demo
public class SchedulePoolTest {
public static void main(String[] args) throws Exception {
ThreadPoolExecutor executorService = new ThreadPoolExecutor(1,5,2L,TimeUnit.SECONDS,new LinkedBlockingQueue<>(2),new TestThreadFactory(),new ThreadPoolExecutor.DiscardPolicy());
class Task implements Runnable{
private String key;
public Task(String key) {
this.key = key;
}
@Override
public void run() {
ThreadLocalContext threadLocalContext = new ThreadLocalContext();
Map map = new HashMap<>();
map.put(key,key);
if (null != ThreadLocalTest.get()){
}else {
threadLocalContext.setMap(map);
ThreadLocalTest.setThreadLocal(threadLocalContext);
}
System.out.println(Thread.currentThread().getName()+"-----"+ JSON.toJSONString(ThreadLocalTest.get()));
// ThreadLocalTest.remove(); }
}
int i=0;
while(true){
executorService.submit(new Task(String.valueOf(i++)));
}
}
}
这个测试线程池,我开起了5个总线程,然后注释了remove,本来逻辑应该是无线递增i的打印输出,但是实际打印的结果是
什么妖魔鬼怪都有,因为本身没有做清除,而线程在执行完成后会返回给池里而不是销毁,所以导致后续的任务都读到了原来的线程,导致threadlocal储存数据异常,而手动remove之后就不会有这个问题
另外针对内存溢出来说,因为ThreadLocal本身其实是ThreadLocalMap的,可以想一下,如果是强引用,那么不管做不做remove,在GC的时候,由于本身是持有了一个映射到对象的key,就一直不会回收,从而导致内存溢出
如果是弱引用,那么在remove后,持有的key指向的对象不存在了,gc时就会自动被回收了