关于ThreadLocal的理解

ThreadLocal 从字面上解释为本地线程,但是使用线程局部变量称呼更为贴切其作用,这也算是名称带来的误解

线程局部变量是什么

每一个线程在里面分配独立的内存空间,互不影响,这是是所谓线程隔离。就每个线程而言,在执行过程中一定会涉及到在多个模块中调用不同的方法,如果需要在这些调用过程中,传递某个变量用于这个过程中使用,没有学习过ThreadLocal的话,我们可能会依赖每个方法的参数和返回值,但是这样改造之后,方法之间就必须互相关联互相耦合,为了在线程运行中传递一个共享的值,这种改造是非常麻烦的。

所以Java提供了ThreadLocal类,这个类可以保持一个变量在一个线程运行过程中共享传递,而且不同线程之间互不影响,线程安全

线程局部变量常用API

实际使用而言,这五个方法较为常用,掌握即可

  • ThreadLocal():用于构造对象,因为ThreadLcoal作为一个,而非接口,所以使用的时候需要通过构造方法new出来

一个空的构造方法,除了构造对象没有做其他任何事,如果需要初始化使用initialValue()方法

  • initialValue():给ThreadLcoal设置一个初值,每个线程第一次get的时候就可以获得这个初值了,如果需要在过程中修改初值使用setInitialValue()方法即可,但是没什么必要,直接set(T)更适合

默认的初始化方法返回null,而且作为一个protected存在,是因为需要使用者重写去设置初值是什么,并返回这个初值

  • set(T):设置值,因为ThreadLcoal带有泛型,所有可以通过泛型限定使用的值

通过当前线程去获取TheadLocalMap, 他的key就是当前ThreadLocal对象引用,value是传递过来需要设定的值,一个线程有且仅有一个ThreadLocalMap与之对应

  • get():获得通过initialValue()设置的初值或者是set(T)设定的值

  • remove():移除设置的值,实际上是通过移除ThreadLocalMap中的key来移除对应的值,还记得key是什么吗?就是当前ThreadLocal对象引用this

注意:在使用完ThreadLcoal之后一定需要调用remove方法移除值,否则可能会有内存泄漏问题,这个后面说

认识下ThreadLocalMap对象

在上面常用API源码认识中,多次看到这个对象,从字面上理解这是个类似于Map的结构,事实上也如此,ThreadLocalMap是作为ThreadLocal的一个静态内部内存在的

弱引用:当没有对象引用时,下一次GC一定会被回收,这里将key(当前ThreadLcoal对象引用,即this)作为弱引用,是为当ThreaedLcoal没有用的时候,可以自动回收

这些静态内部类没有访问修饰符,表示这能在本类本包中使用

成员变量

上面说ThreadLcoalMap很类似MAp,实际上类似与HashMap更为准确,看一下它的几个成员变量

构造方法

hash冲突的解决方案

firstKey.threadLocalHashCode:这里用的是ThreadLcoal的一个成员变量,每次调用都会触发nextHashCode()方法进行一个特殊值累加

这里涉及到处理哈希冲突的解决方案,不同于HashMAp链地址法(当hash值计算相同发生冲突时,将元素依次链接形成一个链表,获取时,通过hash值找到这个链表所在的槽,再通过equals在链表上依次比较查找元素)

ThreadLocalMap使用的方案:当发生冲突时,将冲突的值往后放到新的槽中,如果有元素则再往后移动,偏移位置的计算就是通过ThreadLcoal.threadLocalHashCode & (len - 1)

设置元素的方法

这里截的set方法源码不全,我们主要看下新元素偏移就可以了

获取元素的方法

设置set和获取getEntry方法都涉及到处理key=null的情况,无论是原本为null或者是GC回收弱引用之后形成的null,这里不再讨论了

移除元素的方法

使用中的注意事项

  1. 我们一般使用ThreadLocal是将其作为private static的成员变量。其中static是重点,静态表示内存中只有一份,因为需要在一个线程中共享变量,如果不使用静态,那么一个线程调用过程中,可能就出现创建多个ThreadLocal的情况(所在类被创建多次),这是没有意义的。设置成static之后表示每一个线程有且只有一个ThreadLocal,同时也有且只有一个ThreadLocalMap对象
  2. 使用完ThreadLocal一定要记得remove,否则会出现内存溢出问题,原因就在于我们将其设置为static,当ThreadLocal不再使用的时候会被GC回收,因为是弱引用,会形成ThreadLcoalMap中为null的key,但是整个静态的ThreadLocal可能因为线程还没有运行完无法销毁,出现线程脏数据导致内存溢出

原文地址:https://www.cnblogs.com/lz2017/p/12441957.html