1. ThreadLocalMap
功能:负责保存数据。
实现:由Entry[] 数组实现。根据ThreadLoal实例计算下标。
声明位置:在Thread中声明ThreadLocalMap类型的threadLocals实例变量。
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals;
}
2. ThreadLoal 是什么?
ThreadLocal实例是当前线程Thread中属性threadLocals的管理者。
3. ThreadLocal的原理
ThreadLocal实例的查询操作是 根据自身实例去当前线程Thread的threadLocals属性去查询。
新增、删除也是如此。
4. 使用场景
全局存储用户信息、解决线程安全问题
在Spring的Web项目中,我们通常会将业务分为Controller层,Service层,Dao层, 我们都知道 @Autowired注解默认使用单例模式 ,那么不同请求线程进来之后,由于Dao层使用单例,那么负责数据库连接的Connection也只有一个, 如果每个请求线程都去连接数据库,那么就会造成线程不安全的问题,Spring是如何解决这个问题的呢?
在Spring项目中Dao层中装配的Connection肯定是线程安全的,其解决方案就是采用ThreadLocal方法,当每个请求线程使用Connection的时候, 都会从ThreadLocal获取一次,如果为null,说明没有进行过数据库连接,连接后存入ThreadLocal中,如此一来,每一个请求线程都保存有一份自己的Connection。于是便解决了线程安全问题
5. ThreadLocal 内存泄漏
java.lang.ThreadLocal.ThreadLocalMap.Entry的key为弱引用。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocal在没有外部强引用时,发生 GC 时会被回收,那么 ThreadLocalMap 中保存的 key 值就变成了 null。
而 Entry 又被 threadLocalMap 对象引用,threadLocalMap 对象又被 Thread 对象所引用,那么线程一直不结束,value对象则会一直存在于内存中,也就导致了内存泄漏,直至 Thread被销毁后,才会被回收。
6. ThreadLocal 如何避免内存泄漏
threadLocal.remove()
使用完ThreadLocal ,最好手动调用 remove() 方法,防止出现内存溢出。