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调用set、get、remove的时候会被清除。
3、ThreadLocal有没有内存泄漏的问题?
由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。
那么怎么避免内存泄漏呢? 每次使用完ThreadLocal,建议调用它的remove()方法,清除数据。