数据结构
https://www.cnblogs.com/wang-meng/p/12856648.html
- 每个线程持有自己的ThreadLocalMap,是一个数组,数组的元素为Entity[]
- Entity包含存入的弱引用的key(threadLocal对象)和value值
- 获取元素时,使用key计算哈希槽位来定位到Entity的下标
常用操作源码
```java /**- 设置当前线程的ThreadLocal变量的副本为指定的值 *
- @param value the value to be stored in the current thread’s copy of this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
elsemap.set(this, value);
}createMap(t, value);
/**
- 返回当前线程的ThreadLocal变量副本的值 *
- @return the current thread’s value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
} return setInitialValue(); }ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; }<a name="Pj2Ix"></a> ### 添加元素的方式&hash冲突 ```java private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1); } private void set(ThreadLocal<?> key, Object value) { ThreadLocal.ThreadLocalMap.Entry[] tab = table; int len = tab.length; //计算索引,上面已经有说过。 int i = key.threadLocalHashCode & (len-1); /**根据获取到的索引进行循环,如果当前索引上的table[i]不为空,在没有return的情况下, * 就使用nextIndex()获取下一个(上面提到到线性探测法)。*/ for (ThreadLocal.ThreadLocalMap.Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); //table[i]上key不为空,并且和当前key相同,更新value if (k == key) { e.value = value; return; } /**table[i]上的key为空,说明被回收了 * 说明改table[i]可以重新使用,用新的key-value将其替换,并删除其他无效的entry*/ if (k == null) { replaceStaleEntry(key, value, i); return; } } //不存在也没有旧元素就创建一个 tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash();//扩容 }hash冲突
通过开放寻址法,找到下一个可以使用的空位,则返回
如果都找不到则扩容
扩容
- 扩容前会对过期的key进行清理
- 如果清理完Entity的size还是大于threshold阈值的3/4,则进行扩容,为原来大小的2倍
内存泄漏
弱引用内存泄露

- key是弱引用,当ThreadLocal被回收时,key就为null
- 但是线程对象Thread会存在于整个线程生命周期,所以value无法回收
为什么key是弱引用
- ThreadLocalMap只能通过ThreadLocal间接调用方法,一旦ThreadLocal被回收,对应的Entity也就无法再获取
- 所以为了在ThreadLocal所在对象被回收后自动回收,设计为弱引用
- 等清理过期key时可以一同清除掉
