ThreadLocal分析

问题:?
  • Java的引用类型有哪几种?
  • 每种引用类型的特点是什么?
  • 每种引用类型的应用场景是什么?
  • Threadlocal应用场景?
  • ThreadLocal会产生内存泄漏?

一、ThreadLocal用来干什么?

目的为不同的线程创建一个变量副本;在多线程环境下可以访问对应的副本,也就是变量在不同的线程中是相互隔离的,是线程独享的

二、引用的分类


空间不够时,软引用指向的对象,在空间不够时会被回收,适合缓存;
弱引用遭遇到GC就会被回收
**虚引用的作用:**主要用来管理堆外内存。例如:当虚引用被回收时,会往队列里通知被回收的消息,这个时候jvm就可以回收,堆外内存了。清理堆外内存

三、源码分析
get、set、remove方法
  public T get() {
        Thread t = Thread.currentThread();
      
      // 从当前线程中 获得ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        if (map != null) {
      
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }



public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
    // 存在就添加 没有就设置初始化方法
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
 }


 public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
 }
3.1 Entry结构

每一个Thread线程都有一个属性名为 threadLocals 的ThreadLocalMap; 这个ThreadLocalMap 的管理者就是:定义的ThreadLocal对象;
ThreadLocalMap的结构; key = ThreadLocal对象; value也就是ThreadLocal定义的值;

    
// threadLocals 属性
ThreadLocal.ThreadLocalMap threadLocals = null;

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;
            }
        }
四、ThreadLocal内存泄漏问题
4.1、关系图(ThreadLocal、Thread、ThreadLocalMap)

4.2 为什么ThreadLocal的entry要使用弱引用?

如果是强引用,那么就算 threadLocalRef对象设置为null,ThreadLocal对象也不会回收,因为此时被entry的key强引用了。这个时候如果Tread线程一直不结束,那么就可能发生** 内存泄漏。**
但是如果是弱引用,下次GC时threadLocal会可以被回收。

4.3 entry的弱引用就一定不会内存泄漏吗?

当threadLocal被回收后,entry的key就是null,此时就无法触及value,那么这一条引用链条就一直存在,导致value无法被回收,依旧可能发生内存泄漏。
建议:再使用完threadLocal时需要调用remove() 方法,方法内部会直接 移除 threadLocal所对应的entry对象;

4.4 如果忘记remove还有补救的措施吗?

CurrentThread依然运行的前提下,就算忘记调用remove方法,弱引用比强引用可以多一层保障:弱引用的ThreadLocal会被回收,对应的value在下一次ThreadLocalMap调用set,get,remove中的任一方法的时候会被清除,从而避免内存泄漏。(前提 弱引用)
源码如下:

// 次方法会在get方法中被调用
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) {
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                  // 如果key为空 就会进行清理
                if (k == null)
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }
小结:

虽然在执行get\set方法特定时机会清理key = null的 value,但是这也是不及时的,而且还是在这特定的调用才会发生。所以还是建议在使用完threadlocal变量后显式调用remove。(可以避免在线程池,线程复用的时候 数据污染)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值