为什么 ThreadLocal 可以做到线程隔离?
先通过一个小例子感受一下:
运行结果:
OK,从效果上看,ThreadLocal 确实是线程隔离的,那么,它是如何做到线程隔离的呢?下面我们扒一扒源码,看看它是如何做到的:
set() 方法的逻辑如下:
获取当前线程
根据当前线程获取一个 ThreadLocalMap 对象
如果 map 不为 null 则保存
如果 map 为 null 则创建一个 map
getMap() 和 createMap() 方法都干了啥呢?我们点进去看:
进入到两个方法内部后发现,不管执行哪个分支,最终是把值保存到了当前线程的 threadLocals 属性中。
查看 Thread 类的源码,你会发现类中定义了一个 threadLocals 属性,且初始值为 null,其类型为 ThreadLocal.ThreadLocalMap。
到此,我们发现了,原来 ThreadLocal 就是把我们要传递的对象放到了当前线程的 threadLocals 属性中。也就是说每个线程在用 ThreadLocal 保存对象时,其实就是将对象放到了当前线程实例对象的 threadLocals 属性里面。这样一来线程之间自然就是互相独立的啦。
再看看 get() 方法:
ThreadLocal 的 get() 方法其实和 set() 方法逻辑很相似,先从当前线程的 threadLocals 属性中取,如果该属性为 null,那么就初始化。
当线程结束时,会调用当前线程实例的 exit() 方法,将 threadLocals 设置为 null,以便垃圾回收器将其回收掉。
最后,有一点需要格外注意:用完 ThreadLocal 一定要记得手动调用 remove() 方法,否则可能会产生脏数据甚至产生内存泄漏。
为啥呢?上面不是说线程结束时,会将 threadLocals 置为 null 吗?
是的,线程结束时,确实会做清理工作。
但,如果线程一直不结束呢?如果线程会被复用呢?比如使用了线程池。
所以,使用 ThreadLocal 一定要手动 remove()。
更多独家精彩内容尽在我的新书《Spring Boot趣味实战课》中。