Threadlocal
ThreadLocal 详解_似寒若暖的博客-CSDN博客_threadlocal
Thread的局部变量,线程局部变量ThreadlocalValue。不同线程之间互不干扰。在线程的生命周期内起作用。
在并发场景中,成员变量是线程不安全的,因为多个线程在同时操作同一个变量。
ThreadLocal使得每个线程都拥有自己独立的变量,竞态条件被彻底消除了,在并发模式下是绝对安全的变量。
Threadlocal的底层实现
解决多线程间共享变量线程安全问题的大杀器——ThreadLocal_threadlocal怎么保证线程安全_YHJ的博客-CSDN博客
1在Thread类中定义了类型为ThreadLocalMap的变量ThreadLocals,是一个定制化的HashMap,初始值为null。
在ThreadLocal类中定义了内部静态类ThreadLocalMap,并且定义了get(), set(T), getMap()对其操作。
相当于,ThreadLocal类管理了所有线程自己的ThreadLocalMap类。
ThreadLocal类提供了对这个ThreadLocalMap类的get和set方法。
而ThreadLocalMap类是通过一个对象数组实现的,Entry[] table
Thread线程类中定义了一个threadlocals成员变量,类型为ThreadLocalMap类(可以理解为定制的HashMap)。key为我们所定义的ThreadLocal变量的this引用,value则为set方法传递的值。
我们先定义一个ThreadLocal变量,该变量将来会存放到线程的threadlocals成员变量(可以理解为定制的HashMap)中。
该变量存放在哪个线程中,取决于是在哪个线程中调用了该变量的set()方法。
比如在线程A中调用了该变量的set()方法,就会把该变量存放到线程A的threadlocals成员变量中。其中key为该变量的this引用,value为该变量的值。
如果同时在线程B中调用了该变量的set()方法,则同样会把该变量存放到线程B的threadlocals成员变量中。其中key为该变量的this引用,value为该变量的值。
这可能是一种不安全的使用方式,因为两个线程的threadLocals中的key和value都一样,特别是value指向了同一个对象变量时,这就导致了线程不安全。
正确的做法是在线程内部定义对象变量。
使用Threadlocal的注意事项
出现内存溢出的场景
如果当前线程一直不消亡,那么这些本地变量会一直存在,造成内存溢出。所以需要在使用完毕后,使用remove()方法删除threadLocals中的本地变量。
线程不安全的场景1
注意:如果使用不当,ThreadLocal可能是线程不安全的。
ThreadLocal不安全的情况举例(附代码) - 坚守梦想 - 博客园 (cnblogs.com)
在这个例子中,两个线程的threadLocals中的key和value都一样,特别是value指向了同一个对象,这就导致了线程不安全。
正确的做法是key可以相同,但是value指向不同的对象。
InheritableThreadLocal
为了解决让子线程能够访问到父线程中的值的问题,lnheritableThreadLocal 应运而生。
lnheritableThreadLocal 继承自 ThreadLocal,并提供了一个新特性:让子线程可以访问在父线程中设置的本地变量值。
问题:在上图中, ThreadLocal 变量在父线程中被设置值后,在子线程中是获取不到的。
原因:在子线程里面调用get()方法时,Thread t = Thread.currentThread() 代码是获取当前线程。当前线程是子线程,而调用set()方法给threadLocal赋值的线程是main,两者是不同的线程。
故子线程调用get方法取得的threadLocal值为null,main线程调用get方法取得的threadLocal值为“hello world”。
解决方案:使用lnheritableThreadLocal类定义成员变量。
InheritableThreadLocal实现子线程可以访问父线程的线程变量的实现原理如下:
1.InheritableThreadLocal类通过重写createMap 和 getMap 方法,让本地变量保存到了当前线程的inheritableThreadLocals变量中。
2.当线程A调用inheritableThreadLocal实例的set()或get()方法时,就会创建当前线程A的inheritableThreadLocals变量。
3.当父线程A创建子线程B时,构造函数会把父线程A中的inheritableThreadLocals变量里面的本地变量值复制一份保存到子线程B的inheritableThreadLocals变量里。
使用InheritableThreadlocal的注意事项
注意:如果使用不当,InheritableThreadLocal可能是线程不安全的。
Typically, though, you should not be using InheritableThreadLocal objects on anything that can change because it is not thread-safe.
线程不安全的场景1
问题:当复制时,是否会出现父线程和子线程的inheritableThreadLocals变量的value指向同一个对象?会。
不仅是父子线程的inheritableThreadLocals变量的value可能指向同一个对象,同一个父线程的多个子线程的inheritableThreadLocals变量的value可能指向同一个对象。
这就导致了线程不安全。
线程不安全的场景2
还有一种场景,导致线程不安全。
线程池使用InheritableThreadLocal出现数据脏乱分析和解决方案 | 易学站 (dzscc.com)
上面的例子的问题在于用了线程池。首先,Thread –> ThreadLocalMap–>Entry–>Value,这条引用链导致Entry不会回收,Value也不会回收;
其次是父线程A是核心线程,生命周期很长。当且只当子线程B第一次从线程池获取,第一次创建子线程B时,子线程B会从主线程A获取inheritableThreadLocals值。
当子线程B第二次从线程池获取,就不再有子线程B从主线程A获取inheritableThreadLocals值这个过程。
当线程池中的线程全部被获取过后,结果导致核心父线程A的inheritableThreadLocals数据会更新,但是所有的子线程数据都一直存放着老数据。


