ThreadLocal相关

1. 是什么及为什么用

防止任务在共享资源上产生冲突的第二种方式是根除对变量的共享。线程本地存储是一种自动化机制,可以为使用变量的每个不同的线程都创建不同的存储。因此,如果你有5个线程都要使用变量x所表示的对象,那线程本地存储就会生成5个用于x的不同的存储块。主要是,它们使得你可以将状态与线程关联起来。

每个线程维护一个ThreadLocalMap类型的ThreadLocals对象,里面可以存放很多Entry,每个Entry的key是ThreadLocal,value为存放的对象。
在这里插入图片描述

2.如何使用

线程进来之后初始化一个可以泛型的ThreadLocal对象,之后这个线程只要在remove之前去get,都能拿到之前set的值

ThreadLocal<String> localObj = new ThreadLocal();
localName.set("object");
String name = localName.get();
localName.remove();

Set()方法源码实现:当使用ThreadLocal存值时,首先是获取到当前线程对象,然后获取到当前线程本地变量Map,最后将当前使用的ThreadLocal和传入的值放到Map中,也就是说ThreadLocalMap中存的key是ThreadLocal对象, 存放的值是自定义对象。

ThreadLocalMap是当前线程Thread一个叫threadLocals的变量中获取的。每个线程Thread都维护了自己的threadLocals变量,所以在每个线程创建ThreadLocal的时候,实际上数据是存在自己线程Thread的threadLocals变量里面的,别人没办法拿到,从而实现了隔离。

public class Thread implements Runnable {
      ……
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;  
     ……

3. 数据结构

ThreadLocalMap的Entry是继承WeakReference(弱引用)的,而且Entry中没有next指针,所以不存在链表。
在这里插入图片描述
不存在链表如何解决哈希冲突:
ThreadLocalMap在存储的时候会给每一个ThreadLocal对象一个threadLocalHashCode,在插入过程中,根据ThreadLocal对象的hash值,定位到table中的位置i。

int i = key.threadLocalHashCode & (len-1);
//key为要新增的ThreadLocal

在这里插入图片描述
如果当前位置是空的,就初始化一个Entry对象放在位置i上;如果位置i不为空,如果这个Entry对象的key正好是即将设置的key,那么就刷新Entry中的value;如果位置i的不为空,而且key不等于Entry的key,那就找下一个空位置,直到为空为止。

4.存放位置

ThreadLocal实例实际上也是被其创建的类持有(更顶端应该是被线程持有),而ThreadLocal的值其实也是被线程实例持有,它们都是位于堆上,只是通过一些技巧将可见性修改成了线程可见。
共享线程的ThreadLocal数据
使用InheritableThreadLocal可以实现多个线程访问ThreadLocal的值,我们在主线程中创建一个InheritableThreadLocal的实例,然后在子线程中得到这个InheritableThreadLocal实例设置的值。
子线程创建时,如果线程的inheritThreadLocals变量不为空,而且父线程的inheritThreadLocals也存在,那么父线程的inheritThreadLocals就会传给当前线程的inheritThreadLocals,从而实现共享。

5.内存泄漏问题

ThreadLocal在保存的时候会把自己当做Key存在ThreadLocalMap中,正常情况应该是key和value都应该被外界强引用才对,但是现在key被设计成WeakReference弱引用了。
在这里插入图片描述
当一个线程调用ThreadLocal的set方法设置变量的时候,当前线程的ThreadLocalMap就会存放一个记录,这个记录的key值为ThreadLocal的弱引用,value就是通过set设置的值。如果当前线程一直存在且没有调用该ThreadLocal的remove方法,如果这个时候别的地方还有对ThreadLocal的引用,那么当前线程中的ThreadLocalMap中会存在对ThreadLocal变量的引用和value对象的引用,是不会释放的,就会造成内存泄漏。

考虑这个ThreadLocal变量没有其他强依赖,如果当前线程还存在,由于线程的ThreadLocalMap里面的key是弱引用,所以当前线程的ThreadLocalMap里面的ThreadLocal变量的弱引用在gc的时候就被回收,但是对应的value还是存在的这就可能造成内存泄漏,因为这个时候ThreadLocalMap会存在key为null但是value不为null的entry项
  解决:在使用完之后及时调用remove()方法(finally中)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值