再次深入探究ThreadLocal,以此记录。
为什么说ThreadLocal叫做线程本地变量,因为ThreadLocal在每个线程中对该变量会创建一个线程共享变量,即每个线程内部都会有一个该变量,且在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。
使用ThreadLocal不外乎3个方法:
threadLocal.set()
threadLocal.get()
threadLocal.remove()
顾名思义,分别是设置、获取、移除当前线程中共享变量的值。
话不多说,直接上源码:
get()方法
跟入getMap(t)方法一探究竟
threadLocals
实际上就是一个ThreadLocalMap对象。
ThreadLocalMap其实就是在ThreadLocal中的一个内部类
setInitialValue()
createMap()
new一个ThreadLocalMap对象返回
set()和remove()方法
至此,真相大白。
真正存储线程共享变量的地方是当前线程对象Thread的成员变量threadLocals,类型是ThreadLocal.ThreadLocalMap,k:ThreadLocal变量,v:线程共享变量
一开始,当前线程中的threadLocals为null,调用set()时,会对其中的threadLocals进行初始化,k就是ThreadLocal,v就是需要存放的线程共享变量
当需要的时候就可以从threadLocals中获取到。
总结:
- 之所以说ThreadLocal线程安全,是因为所需要存储的线程共享变量,都是存放在当前线程的threadLocals中。
- 选择ThreadLocal作为threadLocals的key,是因为每个线程中可有多个threadLocal变量。
- 在进行get之前,必须先set,否则会报空指针异常;
使用ThreadLocal的好处:
- 在多线程的环境中,保证当前线程的线程共享变量不被其他线程干扰,即不存在线程安全问题。
- 当线程死去时,threadLocals也就被销毁。
- threadLocals的k-v数量由ThreadLocal对象的数量决定,比起Thread数量,ThreadLocal数量较少,性能更好。
关于ThreadLocalMap<ThreadLocal, Object>弱引用问题:
当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在threadLocals的键值对,造成内存泄露。
(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。
虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用。
为了防止弱引用问题(threadLocals键值对没有被即使回收,造成内存泄漏)的出现,我们有两种手段:
1、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;
2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。
例子为证:
结果: