两大使用场景-ThreadLocal的用途
场景1: 每个线程需要一个独享的对象(通常是工具类,典型需要使用的类有SimpleDateFormat和Random)
场景2: 每个线程内需要保存全局变量(例如在拦截器中获取用户信息),可以让不同方法直接使用,避免参数传递的麻烦。
ThreadLocal
数据结构
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
主要方法介绍
- T initialValue():
- 初始化 void set(T t):
- 为这个线程设置一新值 T get():得到这个线程对应的value。如果是首次调用get()。则会调用initialize来得到这个值
- void remove(): 删除这个线程得到的值
ThreadLocal使用问题内存泄露
什么是内存泄露
某个对象不再有用,但是占用的内存却不能被回收。
Value的泄露
ThreadLocalMap 中使用的 key 为弱引用,而 value 是强引用
弱引用的对象拥有更短暂的生命周期,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会进行回收
所以,如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。这样一来,ThreadLocalMap 中就会出现 key 为 null 的 Entry。假如我们不做任何措施的话,value 永远无法被GC 回收,这个时候就可能会产生内存泄露。
ThreadLocalMap 实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。使用完 ThreadLocal 方法后 最好手动调用remove()方法
如何避免内存泄露呢?
调用remove方法,就会删除对应的Entry对象,可以避免内存泄露,所以使用完ThreadLocal之后,应该调用remove方法。
key为什么要设置成弱引用?
ThreadlocalMap是和线程绑定在一起的,如果这样线程没有被销毁,而我们又已经不会再引用,那么key-value的键值对就会一直在map中存在,这对于程序来说,就出现了内存泄漏。
为了避免这种情况,只要将key设置为弱引用,那么当发生GC的时候,就会自动将弱引用给清理掉,也就是说:假如某个用户A执行方法时产生了一份threadlocalA,然后在很长一段时间都用不到threadlocalA时,作为弱引用,它会在下次垃圾回收时被清理掉。
value为什么不设置成弱引用?
如果是个弱引用,一次gc,entry对象就被回收了, 而threadlocalmap的key被threadlocal变量强引用,还存在。 就导致,通过key查到value为null