ThreadLocal源码分析:(二)get()方法
2018-03-27 16:14
316 查看
在ThreadLocal的get(),set()的时候都会清除线程ThreadLocalMap里所有key为null的value。
而ThreadLocal的remove()方法会先将Entry中对key的弱引用断开,设置为null,然后再清除对应的key为null的value。
本文分析get方法
ThreadLocal类的get方法
关键逻辑就是去当前线程的ThreadLocalMap中获取对应此ThreadLocal对象的entry,如果获取到了就返回entry的value。否则返回调用setInitialValue方法的结果。
代码1
ThreadLocal.ThreadLocalMap类的getEntry方法
如果在key计算hash的位置上直接命中查询,直接返回该entry,否则调用getEntryAfterMiss并返回结果。
代码2
ThreadLocal.ThreadLocalMap类的getEntryAfterMiss方法
在从第i个entry向后遍历的过程中,找到对应的key的entry就直接返回,如果遇到key为null的entry,则调用expungeStaleEntry方法进行清理。
代码3
ThreadLocal.ThreadLocalMap类的expungeStaleEntry方法
expungeStaleEntry方法不止清理了staleSlot位置上的entry,还把staleSlot之后的key为null的entry都清理了,并且顺带将一些有哈希冲突的entry给填充回可用的index中。
代码4
ThreadLocal类的setInitialValue方法
setInitialValue方法里面,真正有难度的就是在map不为null时要去调用set方法了。这种情况会在key(也就是ThreadLocal对象)对应的entry已经被清理过后出现,也有可能是一个没有设置过值的ThreadLocal对象来调用get方法,就会进入到这层逻辑。关于ThreadLocalMap的set方法,在另一篇笔记ThreadLocal源码分析:(一)set(T value)方法中有分析过了,这里就不再贴了。
ThreadLocal的get方法,也可能会触发ThreadLocalMap的清理方法,将ThreadLocalMap中key为null的entry给清理掉,方便GC来回收内存。
系列文章链接:
ThreadLocal源码分析:(一)set(T value)方法
ThreadLocal源码分析:(二)get()方法
Thr
9b87
eadLocal源码分析:(三)remove()方法
而ThreadLocal的remove()方法会先将Entry中对key的弱引用断开,设置为null,然后再清除对应的key为null的value。
本文分析get方法
ThreadLocal类的get方法
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); // 获取线程t中的ThreadLocalMap if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); // 获取entry,见代码1 if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); // 没有找到对应的值,调用setInitialValue方法并返回初始值,见代码4 }
关键逻辑就是去当前线程的ThreadLocalMap中获取对应此ThreadLocal对象的entry,如果获取到了就返回entry的value。否则返回调用setInitialValue方法的结果。
代码1
ThreadLocal.ThreadLocalMap类的getEntry方法
private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) // 在key计算hash的位置上直接命中查询,直接返回该entry return e; else return getEntryAfterMiss(key, i, e); // 没有直接命中,调用getEntryAfterMiss,见代码2 }
如果在key计算hash的位置上直接命中查询,直接返回该entry,否则调用getEntryAfterMiss并返回结果。
代码2
ThreadLocal.ThreadLocalMap类的getEntryAfterMiss方法
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { // 从i位置开始遍历,寻找key能对应上的entry ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) expungeStaleEntry(i); // 遇到key为null的entry,调用expungeStaleEntry方法,见代码3 else i = nextIndex(i, len); e = tab[i]; } return null; // 实在没有找到,只能返回null了 }
在从第i个entry向后遍历的过程中,找到对应的key的entry就直接返回,如果遇到key为null的entry,则调用expungeStaleEntry方法进行清理。
代码3
ThreadLocal.ThreadLocalMap类的expungeStaleEntry方法
private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; // expunge entry at staleSlot tab[staleSlot].value = null; tab[staleSlot] = null; size--; // 以上代码,将entry的value赋值为null,这样方便GC时将真正value占用的内存给释放出来;将entry赋值为null,size减1,这样这个slot就又可以重新存放新的entry了 // Rehash until we encounter null Entry e; int i; for (i = nextIndex(staleSlot, len); // 从staleSlot后一个index开始向后遍历,直到遇到为null的entry (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal<?> k = e.get(); if (k == null) { // 如果entry的key为null,则清除掉该entry e.value = null; tab[i] = null; size--; } else { int h = k.threadLocalHashCode & (len - 1); if (h != i) { // key的hash值不等于目前的index,说明该entry是因为有哈希冲突导致向后移动到当前index位置的 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) // 对该entry,重新进行hash并解决冲突 h = nextIndex(h, len); tab[h] = e; } } } return i; // 返回经过整理后的,位于staleSlot位置后的第一个为null的entry的index值 }
expungeStaleEntry方法不止清理了staleSlot位置上的entry,还把staleSlot之后的key为null的entry都清理了,并且顺带将一些有哈希冲突的entry给填充回可用的index中。
代码4
ThreadLocal类的setInitialValue方法
private T setInitialValue() { T value = initialValue(); // initialValue()方法直接返回null Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); // 调用ThreadLocalMap的set方法 else createMap(t, value); // 创建新的ThreadLocalMap,并将value添加进去 return value; }
setInitialValue方法里面,真正有难度的就是在map不为null时要去调用set方法了。这种情况会在key(也就是ThreadLocal对象)对应的entry已经被清理过后出现,也有可能是一个没有设置过值的ThreadLocal对象来调用get方法,就会进入到这层逻辑。关于ThreadLocalMap的set方法,在另一篇笔记ThreadLocal源码分析:(一)set(T value)方法中有分析过了,这里就不再贴了。
ThreadLocal的get方法,也可能会触发ThreadLocalMap的清理方法,将ThreadLocalMap中key为null的entry给清理掉,方便GC来回收内存。
系列文章链接:
ThreadLocal源码分析:(一)set(T value)方法
ThreadLocal源码分析:(二)get()方法
Thr
9b87
eadLocal源码分析:(三)remove()方法
相关文章推荐
- jQuery原型属性constructor,selector,length,jquery和原型方法size,get,toArray源码分析
- HashMap put、get方法源码分析
- CurrentHashMap 源码分析 initTable(),get() 方法
- jQuery源码分析之$.get/$.post/serialize/serializeArray方法详解
- 随笔:深入理解HashMap——put和get方法的源码分析
- Object源码研究2——getClass()方法分析
- JAVA源码分析之---Object类(一)---registerNatives,getClass方法的使用
- jQuery源码分析之jQuery.eq()和jQuery.get()方法比较
- ThreadLocal源码分析:(三)remove()方法
- jQuery原型方法.pushStack源码分析
- Qt之使用setWindowFlags方法遇到的问题(追踪进入QWidget的源码分析原因,最后用WINAPI解决问题)good
- Google Test(GTest)使用方法和源码解析——自定义输出技术的分析和应用
- jQuery源码解读之removeClass()方法分析
- java的Arrays.sort(Object[] a)方法源码分析
- monkey源码分析之事件注入方法变化
- 【Java源码分析】Java复制数组的方法
- linux2.6.12 下s3c2440 camera接口 源码分析和个人思考之 read方法篇
- mybatis源码分析(方法调用过程)
- MapReduce中一次reduce方法的调用中key的值不断变化分析及源码解析
- 【Android】ArrayList通过remove方法删除元素对象源码分析