方法
set()
获取当前线程 根据线程从map中获取 value 如果换取不到则创建一个map key是当前线程 valua 是set的值
根据hashcode 找下标 然后拿到下标 从i 开始进行遍历进行线性探索 找如果当前下标已经有值了 则进行替换 如果没有值的话则添加
get()
remove()
withInitial()
原理
线性探索
用来解决hash冲突的一种策略 是一种开放寻址的策略
通过hash 函数把key映射到hash 表中的一个位置来访问记录 从而加快查找的速度 存放记录的数据就是hash表(散列表)
当我们针对一个key通过hash函数计算产生一个位置 而这个位置在hash表中已经被另外一个键值对占比时 线性探索就可以解决这种情况
写入:查找hash表中冲突单元最近的空闲单元 把新的键值插入这个空闲单元
查找:从根据 hash函数计算得到的位置开始往后查找 直到找到与key对应的value或找到空的单元
就是个map key是指向当前 ThreadLocal实例的弱引用 value就是数据
ThreadLocalMap中的弱引用
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}
set()
public void set(T value) {//拿到当前线程Thread t = Thread.currentThread();//根据当前线程找当前线程的 ThreadLocalMapThreadLocalMap map = getMap(t);//不等于空 直接setif (map != null) key ThreadLocal的弱引用的实例map.set(this, value);else//直接创建createMap(t, value);}
//t是当前线程 t.threadLocals 表示当前线程的ThreadLocalMap// ThreadLocalMap 的key 是this 表示当前对象 当前对象是 ThreadLocal value 是输入的值void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}
private void set(ThreadLocal<?> key, Object value) {// We don't use a fast path as with get() because it is at// least as common to use set() to create new entries as// it is to replace existing ones, in which case, a fast// path would fail more often than not.Entry[] tab = table;int len = tab.length;//根据hash code找数组的下标int i = key.threadLocalHashCode & (len-1);//从i开始遍历 直到最后一个 线性探索for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();//如果key相等则覆盖if (k == key) {e.value = value;return;}//如果key 为null 用新key value覆盖if (k == null) {//覆盖 并清理 key== null的数据replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;//超过阈值不需要扩容if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();}
向前查找失效的entry向后查找可以被覆盖的entry
向前查找有失效的enrty 向后未找到可以覆盖的Entry
向前未查找到失效的entry 向后找到可以覆盖的Entry
向前未查找到失效的enrty 向后未查找到失效的entry
private void replaceStaleEntry(ThreadLocal<?> key, Object value,int staleSlot) {Entry[] tab = table;int len = tab.length;Entry e;// Back up to check for prior stale entry in current run.// We clean out whole runs at a time to avoid continual// incremental rehashing due to garbage collector freeing// up refs in bunches (i.e., whenever the collector runs).//向前扫描 查找最前面无效的keyint slotToExpunge = staleSlot;for (int i = prevIndex(staleSlot, len);(e = tab[i]) != null;i = prevIndex(i, len))if (e.get() == null)//循环遍历 定位最前面无效的keyslotToExpunge = i;// Find either the key or trailing null slot of run, whichever// occurs first//从i开始向后查找 遍历数组的最后一个Entryfor (int i = nextIndex(staleSlot, len);(e = tab[i]) != null;i = nextIndex(i, len)) {ThreadLocal<?> k = e.get();// If we find key, then we need to swap it// with the stale entry to maintain hash table order.// The newly stale slot, or any other stale slot// encountered above it, can then be sent to expungeStaleEntry// to remove or rehash all of the other entries in run.//找到匹配的key之后if (k == key) {e.value = value;//更新 value值tab[i] = tab[staleSlot];tab[staleSlot] = e;// Start expunge at preceding stale entry if it exists//如果最前面无效的key 和当前的key相同 责任将 i作为起点开始清理if (slotToExpunge == staleSlot)slotToExpunge = i;//清理cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);return;}// If we didn't find stale entry on backward scan, the// first stale entry seen while scanning for key is the// first still present in the run.//如果当前的slot已经无效 并且在向前扫描的过程中没有无效的slot 则更新slotToExpunge当前位置if (k == null && slotToExpunge == staleSlot)slotToExpunge = i;}// If key not found, put new entry in stale slottab[staleSlot].value = null;//如果key对应的value不存在 则直接放一个新的Entrytab[staleSlot] = new Entry(key, value);// If there are any other stale entries in run, expunge them//如果有任何无效的slot 则做一次清理if (slotToExpunge != staleSlot)cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);}
get()
public T get() {Thread t = Thread.currentThread();//根据当前线程拿到一个 ThreadLocalMapThreadLocalMap map = getMap(t);if (map != null) {//根据当前的ThreadLocal实例的软引用 拿valueThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}
private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value;}
ThreadLocal内存泄漏
ThreadLocal每个线程用完后 都需要调用 remove()方法防止内存泄漏
