[Java-Thread]ThreadLocal

229 阅读2分钟

ThreadLocal

  • 需求:多线程中,每条线程都需要拥有一个同名变量,该变量在不同线程中独立存取,互不影响。
  • 作用:针对同一个变量,ThreadLocal类可以为每一个线程创建一个副本。 这样,该变量就成了每个线程的“局部变量”,存取互不影响,保证线程安全(ThreadLocal目的并不是为了实现对一个变量的互斥访问)
  • 原理:在ThreadLocal类中定义了一个静态内部类ThreadLocalMap,每一个Thread都有一个ThreadLocalMap类型的变量threadLocals,就是用threadLocals来存储每一个线程的变量副本。threadLocals内部有一个Entry数组,我们根据键值线程对象,来找到对应线程的变量副本。
  • 参考:内存泄露的原因找到了,罪魁祸首居然是Java TheadLocal

1. ThreadLocal的使用

场景:

  • 当需要存储线程私有变量的时候。
  • 当需要实现线程安全的变量时。
  • 当需要减少线程资源竞争的时候。

常用使用方法如下:

// new
ThreadLocal<BigObject> threadLocal = new ThreadLocal<>();
// set存
threadLocal.set(new BigObject());
// get取
BigObject bo = threadLocal.get();
// remove删
threadLocal.remove();

2、 ThreadLocal的数据结构

  • 每个线程是一个Thread实例,每个线程实例都对应一个TheadLocalMap实例
  • 其内部维护一个threadLocals实例成员,其类型是ThreadLocal.ThreadLocalMap
Thread : ThreadLocal.ThreadLocalMap threadLocals
  • ThreadLocal本身并不是一个容器,我们存取的value实际上存储在ThreadLocalMap中,ThreadLocal只是作为TheadLocalMap的key,最终存储在Entry[] table数组中。
ThreadLocalMap map = Thread.currentThread().threadLocals;
map.put(this,value);
map.get(this);
  • 其中TheadLocalMap中的Entry是一对key-value,为WeakReference的子类
static class Entry extends WeakReference<ThreadLocal<?>>

引用ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用setgetremove的时候会被清除。

3、ThreadLocal有没有内存泄漏的问题?

由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。

那么怎么避免内存泄漏呢? 每次使用完ThreadLocal,建议调用它的remove()方法,清除数据。