threadlocal定义:每个线程有自己的数据 其他的线程拿不到
每个线程本身有一个threadlocals属性(类型是ThreadLocalMap),map中存储的entry的key是当前线程的threadlocal对象,value是自定义的值
ThreadLocalMap中的静态内部类Entry结构
static class Entry extends WeakReference<ThreadLocal<?>> { // 当前线程关联的threadlocal的value值 Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
注意entry的key是个弱引用,value是个强引用
那么为什么entry要设计成弱引用呢?
threadlocal对象被gc回收的时候 线程的threadlocalmap中相对应的entry的key变为null
可以区分哪些entry是过期的
注意:ThreadlocalMap类是静态内部类 是共享的数据
threadlocalmap放入元素的时候如果发生hash冲突 是不会转化为链表存储的,会向slot后查找可以存放的桶位,查找的时候也是这样
查找方法最核心的方法是:
1 private int expungeStaleEntry(int staleSlot) { // 这个方法是用来优化查找 优化过期数据,目的是帮助过期数据让gc回收,让hash冲突的entry找到尽可能正确的位置
// 执行这个方法的时候说明查找的entry没找到并且key等于null staleSlot代表key为null的索引 2 Entry[] tab = table; 3 int len = tab.length; 4 5 // expunge entry at staleSlot 6 tab[staleSlot].value = null; 7 tab[staleSlot] = null; 8 size--; 9 10 // Rehash until we encounter null 11 Entry e; 12 int i; 13 for (i = nextIndex(staleSlot, len); 14 (e = tab[i]) != null; 15 i = nextIndex(i, len)) { //查找到entry不为null的索引 16 ThreadLocal<?> k = e.get(); 17 if (k == null) { //key失效了 18 e.value = null; 19 tab[i] = null; 20 size--; 21 } else { 22 // 查到到的entry不是过期数据 23 int h = k.threadLocalHashCode & (len - 1); 24 // 并且就是位置不正确的entry 正确位置是h i是查找出来的entry的当前位置 25 if (h != i) {// h!=i说明位置发生了偏移了 26 // 将entry当前位置设置为null 也就是i设置为null 27 tab[i] = null; 28 29 // Unlike Knuth 6.4 Algorithm R, we must scan until 30 // null because multiple entries could have been stale. 31 // 如果h位置被占用了 就往后查找最靠近正确位置的index 找到就存入数据 32 while (tab[h] != null) 33 h = nextIndex(h, len); 34 tab[h] = e; 35 } 36 } 37 } 38 return i; 39 }
threadlocal的entry数组中entry的size大于等于数组长度的3分之2进行rehash,扩容rehash前回先清除一下key失效的entry,如果清理过后entry的数量还大于等于threhold的4分之3
进行数组扩容并重新计算entry的下标