对ThreadLocal的理解个人
ThreadLocal提供一个方便的方式,可以根据不同的线程存放一些不同的特征属性,可以方便的在线程中进行存取;
简单一句话,自己的线程里的东西自己玩,别人不能动。达到隔离的效果,就不存在线程安全的问题了;
举例子:1000个线程任务打印时间:
结果:用dateFormat的时候,会出现打印出相同的时间的结果,原因是所有线程共用一个SimpleDateFormat对象的时候,它发生了线程安全的问题,而用dateFormatThreadLocal解决了;
原理;先沾出来ThreadLocal的源码
简化下就如下:
Thread.currentThread().threadLocals.set(this,value);
Thread.currentThread()获取当前的线程
threadLocals就是每个线程对象中用于存放局部对象的map
threadLocals可以简单粗暴的理解为,每一个线程都自带一个map,map的key为dateFormatThreadLocal,value为SimpleDateFormat对象;这样这个对象就隔离了,自己用自己的;
ThreadLocal就是一个标记的作用,当我们在线程中使用ThreadLocal的set()或者get()方法时,其实是在操作我们线程自带的threadLocals这个map,多个线程的时候自然就有多个map,这些map互相独立,但是,这些map都是根据一个ThreadLocal对象(因为它是静态的)来作为键存放。
这样可以在多个线程中,每个线程存放不一样的变量,我们通过一个ThreadLocal对象,在不同的线程(通过Thread.currentThread()获取当前线程)中得到不同的值(不同线程的threadLocals不一样)。
为什么threadLocals要是一个map呢?
因为我们可能会在一个类中声明多个ThreadLocal的实例,这样就有多个标记,所以要使用map对应。
ThreadLocal这么吊了,用它时候要注意了,他可能产生内存泄漏!!
threadLocal,threadLocalMap,entry之间的关系如下图所示:
为什么使用弱引用?
通过threadLocal,threadLocalMap,entry的引用关系看起来threadLocal存在内存泄漏的问题似乎是因为threadLocal是被弱引用修饰的。那为什么要使用弱引用呢?
如果使用强引用
假设threadLocal使用的是强引用,在业务代码中执行threadLocalInstance==null
操作,以清理掉threadLocal实例的目的,但是因为threadLocalMap的Entry强引用threadLocal,因此在gc的时候进行可达性分析,threadLocal依然可达,对threadLocal并不会进行垃圾回收,这样就无法真正达到业务逻辑的目的,出现逻辑错误
如果使用弱引用
假设Entry弱引用threadLocal,尽管会出现内存泄漏的问题,但是在threadLocal的生命周期里(set,getEntry,remove)里,都会针对key为null的脏entry进行处理。
从以上的分析可以看出,使用弱引用的话在threadLocal生命周期里会尽可能的保证不出现内存泄漏的问题,达到安全的状态。
- 每次使用完ThreadLocal,都调用它的remove()方法,清除数据。
- 在使用线程池的情况下,没有及时清理ThreadLocal,不仅是内存泄漏的问题,更严重的是可能导致业务逻辑出现问题。所以,使用ThreadLocal就跟加锁完要解锁一样,用完就清理。
引用:https://www.jianshu.com/p/dde92ec37bd1