ThreadLocal详解

86 阅读2分钟

1. 两大使用场景——Thread Local的用途

  • 每个线程需要一个独享的对象(通常是工具类,典型需要使用的类如SimpleDateFormatRandom)。
  • 每个线程内需要保存全局变量,可以让不同方法直接使用,避免参数传递的麻烦,例如在拦截器中获取用户信息。

2. ThreadLocal的两个作用

  • 让某个需要用到的对象在线程间隔离,每个线程都有自己的独立对象。
  • 在任何方法中都可以轻松获取到该对象。

ThreadLocal提供了initialValueset等方法:

  • initialValue:在第一次初始化ThreadLocal时使用,一般是常用的对象。
  • set:用于一些随时可能生成的对象,例如拦截器生成的用户信息。

3. ThreadLocal的好处

  • 线程安全:不需要加锁,提高执行效率。
  • 更高效地利用内存、节省开销:相比于每个任务都新建一个对象,使用ThreadLocal可以节省内存和开销。
  • 免去传参的繁琐:无论是工具类还是用户信息,通过ThreadLocal可以在任何地方直接获取,不需要每次都传递相同的参数。

4. ThreadLocal的原理

ThreadLocal底层原理是一个ThreadLocalMap,为线程独有,其中keyThreadLocalValue是存入的值。在任何方法中都可以轻松获取到该对象。

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,因为框架会处理好线程安全和内存泄漏等问题。