1. 两大使用场景——Thread Local的用途
- 每个线程需要一个独享的对象(通常是工具类,典型需要使用的类如
SimpleDateFormat和Random)。 - 每个线程内需要保存全局变量,可以让不同方法直接使用,避免参数传递的麻烦,例如在拦截器中获取用户信息。
2. ThreadLocal的两个作用
- 让某个需要用到的对象在线程间隔离,每个线程都有自己的独立对象。
- 在任何方法中都可以轻松获取到该对象。
ThreadLocal提供了initialValue、set等方法:
initialValue:在第一次初始化ThreadLocal时使用,一般是常用的对象。set:用于一些随时可能生成的对象,例如拦截器生成的用户信息。
3. ThreadLocal的好处
- 线程安全:不需要加锁,提高执行效率。
- 更高效地利用内存、节省开销:相比于每个任务都新建一个对象,使用
ThreadLocal可以节省内存和开销。 - 免去传参的繁琐:无论是工具类还是用户信息,通过
ThreadLocal可以在任何地方直接获取,不需要每次都传递相同的参数。
4. ThreadLocal的原理
ThreadLocal底层原理是一个ThreadLocalMap,为线程独有,其中key为ThreadLocal,Value是存入的值。在任何方法中都可以轻松获取到该对象。
5. 内存泄漏
ThreadLocal的内存泄漏可能发生在长时间运行的线程未终止的情况下。其原因是ThreadLocalMap中的Entry节点继承了弱引用。如果线程不终止,value就不能被回收,可能导致内存泄漏。
6. 避免内存泄漏(阿里规约)
调用remove方法,删除对应的Entry对象,可以避免内存泄漏。使用完ThreadLocal后,应该调用remove方法。
7. ThreadLocal空指针异常
- 如果是基本类型的包装类作为泛型,在获取使用基本类型时,需要先调用
set,以及返回类型不要是基本数据类型。 - 如果
ThreadLocal中设置的是共享对象,多个线程的ThreadLocal.get()取得的还是这个共享对象本身,存在并发访问问题。
8. 共享对象
如果在每个线程中ThreadLocal.set()进去的对象本来就是多线程共享的同一个对象(如static对象),那么多个线程的ThreadLocal.get()取得的还是这个共享对象本身,存在并发访问问题。
9. 优先使用框架的支持
在Spring等框架中,如果可以使用RequestContextHolder等支持,就不需要手动维护ThreadLocal,因为框架会处理好线程安全和内存泄漏等问题。