画图在什么网站上做兼职,广告词,做深度报道的网站,网站建设 万户建站什么是ThreadLocal#xff1f;
ThreadLocal是线程变量#xff0c;每个线程可以在一个ThreadLocal里面存放一个变量#xff0c;这个变量是线程安全的#xff0c;除了ThreadLocal还可以用栈的本地变量或者锁来保证线程安全#xff0c;并且可以用于方法间的数据传递。
Thre…什么是ThreadLocal
ThreadLocal是线程变量每个线程可以在一个ThreadLocal里面存放一个变量这个变量是线程安全的除了ThreadLocal还可以用栈的本地变量或者锁来保证线程安全并且可以用于方法间的数据传递。
ThreadLocalMap是ThreadLocal的一个内部类用于保存数据key是ThreadLocal的一个弱引用可以有很多个ThreadLocal那如果是强引用就不会被回收了value是存放的变量值 // 强引用ThreadLocalObject threadLocal new ThreadLocal();threadLocal.get();// 弱引用new ThreadLocal().get();
Thread类有一个类型为ThreadLocal.ThreadLocalMap的实例变量threadLocals也就是说每个线程有自己的ThreadLocalMap
简单来说就是每个线程一个ThreadLocalMap存放很多个 不同的ThreadLocal不同的变量值 应用场景
保存某个信息在线程的任意时刻可以使用如保存用户user信息可以获取userId或者其他什么信息 源码解析
hash冲突
和HashMap的链地址法不同ThreadLocal采用nextIndex()和prevIndex()往前后找位置 nextIndex()和prevIndex()
向后或者向前遍历 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);} set() 计算下标位置 下标位置就是空白的则直接加入 下标位置有节点则往后遍历如果发现key相同的节点进行值替换 如果没有过期节点一直找到空白位置 如果有过期节点进行替换并且进行清理 public void set(T value) {Thread t Thread.currentThread();ThreadLocalMap map getMap(t);if (map ! null) {map.set(this, value);} else {createMap(t, value);}}private void set(ThreadLocal? key, Object value) {Entry[] tab table;int len tab.length;// 对于同一个线程的下标位置都会相同 int i key.threadLocalHashCode (len-1);// 找到合适的位置存放新节点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说明节点被回收了替换之if (k null) {replaceStaleEntry(key, value, i);return;}}// 找到了空白的位置准备加入新节点tab[i] new Entry(key, value);int sz size;// 超过阈值进行扩容if (!cleanSomeSlots(i, sz) sz threshold)rehash();} replaceStaleEntry()
过期节点替换和垃圾清理往前找到空白前的第一个过期节点作为开始清理点
slotToExpunge从这个位置开始垃圾清理
staleSlot需要替换的过期节点位置 private void replaceStaleEntry(ThreadLocal? key, Object value,int staleSlot) {Entry[] tab table;int len tab.length;Entry e;// 遍历检测还有没有其它过期的元素直到null用slotToExpunge记录开始点之后用于清理数据int slotToExpunge staleSlot;for (int i prevIndex(staleSlot, len);(e tab[i]) ! null;i prevIndex(i, len))if (e.get() null)// 当前节点过期了更新slotToExpungeslotToExpunge i;// 从staleSlot往后遍历for (int i nextIndex(staleSlot, len);(e tab[i]) ! null;i nextIndex(i, len)) {ThreadLocal? k e.get();// 这里主要是为了配合调用者方法也有一个key相同的处理方式if (k key) {e.value value;tab[i] tab[staleSlot];tab[staleSlot] e;// 相等的意思是staleSlot前边没有过期节点从当前位置开始垃圾清理if (slotToExpunge staleSlot)slotToExpunge i;cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);return;}// 更新slotToExpungeif (k null slotToExpunge staleSlot)slotToExpunge i;}// 替换staleSlot位置为新节点也是这个方法的主要目的tab[staleSlot].value null;tab[staleSlot] new Entry(key, value);// 相等的意思是staleSlot前边没有过期节点从当前位置开始垃圾清理if (slotToExpunge ! staleSlot)cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);} 垃圾清理
expungeStaleEntry()
线性清理遇到空白则停止清理这里的staleSlot实际上是传进来的slotToExpunge private int expungeStaleEntry(int staleSlot) {Entry[] tab table;int len tab.length;// expunge entry at staleSlottab[staleSlot].value null;tab[staleSlot] null;size--;// Rehash until we encounter nullEntry e;int i;// 往后遍历for (i nextIndex(staleSlot, len);(e tab[i]) ! null;i nextIndex(i, len)) {ThreadLocal? k e.get();// 直接清理if (k null) {e.value null;tab[i] null;size--;} else {// 进行rehash重新存放位置int h k.threadLocalHashCode (len - 1);if (h ! i) {tab[i] null;// Unlike Knuth 6.4 Algorithm R, we must scan until// null because multiple entries could have been stale.while (tab[h] ! null)h nextIndex(h, len);tab[h] e;}}}return i;} cleanSomeSlots()
每次循环n/2n是table[]数组的长度直到n0停止清理也是线性清理 private boolean cleanSomeSlots(int i, int n) {boolean removed false;Entry[] tab table;int len tab.length;do {i nextIndex(i, len);Entry e tab[i];if (e ! null e.get() null) {n len;removed true;i expungeStaleEntry(i);}} while ( (n 1) ! 0);return removed;} 扩容 // threshold 默认 2/3 * lenif (!cleanSomeSlots(i, sz) sz threshold)rehash();private void rehash() {// 从开头开始清理所有过期节点然后移动零散节点到一起expungeStaleEntries();// 因为有些过期节点被清理减少扩容的阈值判断if (size threshold - threshold / 4)// 2倍扩容重新rehashresize();}