ThreadLocal的定义
- ThreadLocal通过为每一个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。
- 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不会影响其他线程所对应的副本。
- 在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的key为线程对象,value为对应的线程的变量副本。
为什么要用ThreadLoacal
Thread类中有个属性threadLocals,这个参数包含着线程的所有成员变量。我们发现Thread并没有提供成员变量threadLocals的设置与访问的方法,那么每个线程的实例threadLocals参数我们如何操作?这时候就需要用ThreadLocal来实现了。
总结来说:ThreadLocal是线程Thread中属性ThreadLocals的管理者。
抽象比喻如下:
1.每个人都有一张银行卡。
2.每个人每张卡都有一定的余额。
3.每个人获取银行卡余额都必须通过该银行的管理系统。
4.每个人都只能获取自己卡持有的余额信息,其他的人不可以访问。
映射到ThreadLocal:
1.card类似于Thread;
2.card余额属性,卡号属性等类似于ThreadLocal内部属性集合threadLocals;
3.cardManager类似于ThreadLocal管理类。
ThreadLocal的实现机制
- 每个线程都拥有一个ThreadLocalMap对象。
- 每一个ThreadLocal对象有一个创建时生成的唯一的HashCode,即nextHashCode(),通过取模确定所在位置。
- 访问一个ThreadLocal变量的值,即是查找ThreadLocalMap中对应的键值对,即key为该
ThreadLocal的键值对; - 由于一个ThreadLocalMap可以拥有多个ThreadLocal,推到可得一个线程可拥有多个ThreadLocal。
ThreadLocal的内存泄漏
首先补充一个知识点:
由于ThreadLocalMap中的entry中的key是弱引用,当每次GC时JVM会主动将无用的弱引用回收掉,因此ThreadLocal外部没有强引用依赖时,就会被自动回收,这样就可能造成ThreadLocal被回收时,相当于将Map中的key设置为null,但问题是该key对应的Entry和value并不会主动被GC回收。
当Entry和value未被主动回收时,除非当前线程死亡,否则线程对于Entry的强引用会一直存在,从而导致内存泄漏。
解决方法:
ThreadLocal使用后务必调用remove方法。