多线程之--ThreadLocal-笔记

多线程中使用ThreadLocal来保存状态变量,来保证线程安全,源码是怎么实现的?

看两个地方来学习一下ThreadLocal:get方法和set方法;

1、ThreadLocal的set方法

先把源码放出来:

public void set(T value) {
   Thread t = Thread.currentThread();
   ThreadLocalMap map = getMap(t);
   if (map != null)
     map.set(this, value);
   else
     createMap(t, value);
 }

获取了当前线程并调用getMap,来看一下getMap干了什么:

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

很简单就是获取了,该线程的threadLocals属性。

继续向下看,map存在就将当前ThreadLocal对象当作key,将value存到当前线程的ThreadLocalMap中;

map不存在时,new了一个ThreadLocalMap出来把null存进去,完毕。

2、ThreadLocal的get方法

先上代码:

public T get() {
   Thread t = Thread.currentThread();
   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();
}

还是先获取当前线程,然后获取ThreadLocalMap,然后当前ThreadLocal对象作为key获取对应的Entry.value。

总结

在Thread对象中持有一个ThreadLocalMap,最终存和取时,将当前ThreadLocal对象作为key取存或取值。

每个线程持有自己的ThreadLocalMap,相互不干扰,保证了线程的安全。

ThreadLocalMap是什么?

ThreadLocal中的一个静态内部类,它实现了键值对的设置和获取。

static class ThreadLocalMap {
。。。
}

使用ThreadLocal存在内存泄露?

先来看一下ThreadLocalMap中又什么东西:

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
         super(k);
         value = v;
    }
 }
private static final int INITIAL_CAPACITY = 16;

private Entry[] table;

private int size = 0;

private int threshold; // Default to 0

这里存在一个奇妙的东西:WeakReference,先解释一下:

WeakReference是Java语言规范中为了区别直接的对象引用(程序中通过构造函数声明出来的对象引用)而定义的另外一种引用关系。WeakReference标志性的特点是:reference实例不会影响到被应用对象的GC回收行为(即只要对象被除WeakReference对象之外所有的对象解除引用后,该对象便可以被GC回收),只不过在被对象回收之后,reference实例想获得被应用的对象时程序会返回null。

每个thread中都存在一个ThreadLocalMap,   Map中的key为一个threadlocal对象. 这个Map使用弱引用只是针对key。

每个key都弱引用指向threadlocal. 当把threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收。

但是,我们的value却不能回收,因为存在一条从current thread连接过来的强引用。

只有当前thread结束以后, current thread就不会存在栈中,强引用断开, Current Thread, Map, value将全部被GC回收。

//这里的弱引用仅针对entry对Threadlocal对象的引用,但对value是强引用
Entry(ThreadLocal<?> k, Object v) {
  super(k);
  value = v;
}

这个线程对象被gc回收,就不会出现内存泄露,但在threadLocal设为null和线程结束这段时间不会被回收的,就发生了我们认为的内存泄露。

最要命的是线程对象不被回收的情况,这就发生了真正意义上的内存泄露。比如使用线程池的时候,线程结束是不会销毁的,会再次使用的。就可能出现内存泄露。

在实现过程中,为避免内存泄露在调用get和set方法时,会对清除线程Map里所有key为null的value。

所以最怕的情况就是,threadLocal对象设null了,开始发生“内存泄露”,然后使用线程池,这个线程结束,线程放回线程池中不销毁,这个线程一直不被使用,或者分配使用了又不再调用get,set方法,那么这个期间就会发生真正的内存泄露。

此处引用:链接:https://www.jianshu.com/p/1b32d72dd08b

使用建议

调用threadlocal的remove方法,把当前ThreadLocal从当前线程的ThreadLocalMap中移除。(包括key,value)。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值