ThreadLocal按名字理解线程本地变量,实现了数据存取的线程隔离。提供了set(),get()和remove()的方法。
有一个静态内部类threadlocalmap,维护了一个元素为Entry<key,value>的数组,key为ThreadLocal的一个弱引用,value为存储的对象。调用ThreadLocal的set()和get()实际上最终操作的是ThreadLocalMap。
线程Thread持有一个ThreadLocal.ThreadLocalMap对象,当用户操作ThreadLocal存取数据的时候,实质上操作的是当前线程Thread对象所持有的ThreadLocalMap,因此实现了线程隔离。
分析ThreadLocal的原理必然离不开ThreadLocalMap,这个才是精髓所在。ThreadLocal可以理解为操作ThreadLocalMap的一个代理。
ThreadLocalMap实现懒加载,也就是说当用户调用,调用set()方法存储数据的时候才初始化。这个类自己实现了一个数组类型的数据结构。数组每一个元素为一个Entry<key,value>类型,通过nextIndex(),preIndex()实现逻辑上的环形数组。
ThreadLocalmLMap,默认创建大小为16,负载因子为大小的2/3,当存储数据超过这个阈值会扩容,变为原来的2倍。
getEntry(),ThreadLocalMap对key取hash值然后与数组长度减一取模,得出值的索引。如果该位置Entry存在且未失效(弱引用可能会被GC),那么直接返回,结束。否则调用getEntryAfterMiss()用线性探测法往后寻找目标Entry。在不为空的Entry中判断,如果是目标entry,那么直接返回,如果不是那么该Entry已失效(弱引用,该Entrykey已被GC回收),则调用方法对无效的Entry进行清理。如果最后都没找到则返回Null。
大小始终是2的N次幂的原因:1.采用的是线性探测法对目标Entry进行存取,2.采用的hash算法得出的hash值与2的N次幂取模得到的结果分布比较均匀,利于线性探测法查找目标Entry,提高效率。
setEntry()方法,也有对Entry状态进行判断,然后做出对应的策略。对失效的会及时清理将其引用置为Null。
threadlocal是否会导致内存泄露的问题。先理清这几个对象的引用关系: 1.栈中threadlocal的引用,引用堆中threadlocal,堆中threadlocal被entry中的key弱引用; 2.栈中thread引用,引用堆中thread,堆中thread引用threadlocalmap,threadlocalmap引用entry;
如果当前threadlocal被弃用,又由于entry中key对threadlocal是弱引用,所以1中的引用链不可达,会被回收。2引用链中可达,不会被回收。但后面如果调用了threadlocal的set()或者get()或者remove()方法,那么都会触发threadlocalmap内部的无效资源清理,如果不调用呢?只能记住每次弃用threadlocal后及时调用它的remove()方法,避免内存泄露。