ThreadLocal介绍

ThreadLocal是JAVA中用于解决线程安全问题的一种机制,它允许创建线程局部变量,即每个线程都有自己独立的变量副本,从而避免了线程间的资源共享和同步问题。

从内存结构图,我们可以看到

  • Thread类中,有个ThreadLocal.ThreadLocalMap的成员变量。
  • ThreadLocalMap内部维护了Entry数组,每个Entry代表一个完整的对象,key是ThreadLocal本身,value是ThreadLocal的泛型对象值。

ThreadLocal的原理

ThreadLocal的实现依赖于Thread类中的一个ThreadLocalMap字段,这是一个存储ThreadLocal变量本身和对应值的映射。

每个线程都有自己的ThreadLocalMap实例,用于存储该线程所持有的所有ThreadLocal变量的值。

当你创建一个ThreadLocal变量的时候,它实际上就是一个ThreadLocal对象的实例,每个ThreadLocal对象都可以存储任意类型的值,这个值对于每个线程来说是独立的。

get()、set()和remove()方法

get()方法

当调用ThreadLocal的get()方法的时候,ThreadLocal会检查当前线程的ThreadLocalMap中是否有与值关联的值。

如果有,就返回该值

如果没有,会调用initialValue()方法初始化该值,然后将其放入ThreadLocalMap中并返回。

如果之前没有设置过值,ThreadLocal会调用initialValue()方法来获取一个默认值。
举个例子:

ThreadLocal<String> threadLocal = new ThreadLocal<String>() {
    @Override
    protected String initialValue() {
        return "default value";
    }
};
String value = threadLocal.get(); // 如果没有设置过值,将返回"default value"

如果当前线程的ThreadLocalMap中没有与该ThreadLocal对象关联的值,get()方法会调用ThreadLocal的initialValue()方法来获取一个默认值,并将这个值存储在ThreadLocalMap中。
如果ThreadLocalMap中已经存在与该ThreadLocal对象关联的值,get()方法会直接返回这个值,而不会调用initialValue()方法。

set()方法

当调用set()方法的时候,ThreadLocal会将给定值与当前线程管来弄起来
也就是在ThreadLocalMap中存储一个键值对,key是ThreadLocal对象本身,value是传入的值。

remove()方法

当调用remove()方法的时候,ThreadLocal会从当前线程的ThreadLocalMap中删除与该ThreadLocal对象关联的值。

可能存在的问题

ThreadLocal对象内部有一个ThreadLocalMap类型的成员变量,这个Map的生命周期和线程本身是绑定的,只要线程不销毁,这个Map就会一直存在。

ThredLocalMap存储数据用的Entry继承了WeakReference,它的结构是这样的:
key是对ThreadLocal对象的弱引用,而value是我们存入的值,它是强引用。

内存泄漏问题

当一个ThreadLocal对象在外部没有了强引用(比如方法执行结束),下一次GC发生的时候,由于Entry的key是弱引用,所以ThreadLocal对象会被回收。
这时,ThreadLocalMap中就会出现key为Null的Entry,这个Entry的value仍然是强引用的,这就导致了内存泄漏。只要线程不结束,ThreadLocalMap就会一直持有这个值,无法被回收,Entry会一直持有这个value的强引用,导致value无法被垃圾回收。如果线程是从线程池中复用的,那么这个线程的生命周期会很长,这些value就会极少称多,最终导致内存泄漏。

因此,为了防止ThreadLocal内存泄漏,在使用完ThreadLocal之后,必须在代码中调用ThreadLocal的remove()方法来清除当前线程中的ThreadLocal变量。

ThreadLocal作用

线程隔离: ThreadLocal为每个线程提供了独立的变量副本,意味着线程之间不会相互影响,可以安全地在多线程环境中使用这些变量,不需要担心数据竞争或者同步问题。

降低耦合度: 在同一个线程内的多个函数或者组件之间,使用ThreadLocal可以减少参数的传递,降低代码之间的耦合度,使得代码更加清晰和模块化。

性能优势: 由于ThreadLocal避免了线程之间的同步开销,所以在大量线程并发执行的时候,相比传统的锁机制,可以提供更好的性能。