ThreadLocal源码分析

来源:互联网 发布:工程造价需要什么软件 编辑:程序博客网 时间:2024/05/29 09:50

源码比较简单,但是里面有很多优化的地方。

TheadLocal.get()的时候实际上拿到当前线程的threadLocalMap,以theadLocal为key,取value的过程。

源码为证:

public T get() {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null)                return (T)e.value;        }        return setInitialValue();    }
但是ThreadLocalMap是一个静态内部类。其中的entry是一个weakReference<ThreadLocal>,其中ThreadLocal是key。所以当GC发生时,无论是minor GC还是major GC,如果threadLocal没有外部强引用,那么threadlocal会被GC掉,但是value是entry的强引用,不会被GC,这样的entry被称为stale Entry。这样存在的一个问题就是在适当的时机从threadlocalmap中删除entry。

来看看ThreadLocal是怎么处理的,判断是否是statle Entry的代码很简单,就是entry.get()是否为空:

private int expungeStaleEntry(int staleSlot) {            Entry[] tab = table;            int len = tab.length;            // expunge entry at staleSlot            tab[staleSlot].value = null;            tab[staleSlot] = null;            size--;            // Rehash until we encounter null            Entry 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 {                    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;        }
可以看到会把从staleSlot到null entry这一段内所有的stale entry都删除掉,并且非stale的entry会重新rehash。保证stale slot所在的run(指两个null entry之间的所有entry)内不再有stale slot。但不是删除map内所有的stale entry。

另外一个删除stale entry的方法是:

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;        }
可以看到内部调用了expungeStaleEntry,但是也有漏掉statle entry的情况,比如说在从i开始的log2(n)都不存在stale entry,但是其他地方存在stale的场景。

在get或者set中会调用上面的两个方法来删除部分stale,但是有可能不是全部。

所以存在内存泄露,记的把TheadLocal设置为static,然后调用remove方法。

0 0
原创粉丝点击