jdk8 ThreadLocal源码解读

jdk8 ThreadLocal源码解读

PS:一直以来都没养成写博客的习惯,总是看了忘,忘了看,印象也不怎么深刻。年纪大了力不从心,哈哈哈,还是要写下来,以备不时之需。

1. 简介

​ 防止任务在共享资源上产生冲突的一种方案是:使用同步,比如各种锁(synchronize, lock等),还有一种方案是不共享数据(栈封闭和ThreadLocal技术)。仅单线程内访问数据就不需要同步,这种技术被称为线程封闭。ThreadLocal通常用来保存一个全局变量,ThreadLocal通过提供get(),set()方法为每个使用变量的线程都保存一份副本,这个变量在不同线程之间互不干扰。即:对于各个线程来说虽然是同名变量,但是这个变量仅仅是同名,它保存的内容在不同线程中可能各不相同,a线程对ThreadLocal变量t的修改操作仅限于在a线程生命周期内可见,b线程有它自己的变量t`,操作结果仅限于线程实例内可见。这样就达到了防止变量对象逃逸,线程封闭以及不共享的效果。

使用场景上,我们通常在web服务器会话管理(每个http请求是一个用户操作出来的,该会话只在当前线程生效),数据库连接会话(每次发起一个数据库查询是由一个特定的connect来提供的,在一次查询中都是由这个connect来服务的)。相关的使用案例,自行百度。我们下面进入本文重点:源码解读。

2.ThreadLocal成员

我们先罗列出来TheadLocal中的方法,类,变量,最后再从常用的方法get(),set(),remove()开始进行源码精读串起来。

字段

//ThreadLocal对象持有的hashCode魔数(这个还有下面的魔数HASH_INCREMENT最后讲)
private final int threadLocalHashCode = nextHashCode();

//类的静态原子对象,用于生成ThreadLocal对象魔数
private static AtomicInteger nextHashCode =new AtomicInteger();

//类的静态原子对象每次自增的gap, 魔数,它等于2^32 * (√5-1)/2 的有符号整数值的正整数部分(2^32*黄金分割点)
private static final int HASH_INCREMENT = 0x61c88647;//1640531527

构造方法

//没什么说的,无参构造
public ThreadLocal(){
}

方法

//获取初始值或set进去的值,返回可能为空(初始值就是null或 key被gc)
public T get(){}

//在线程中将一个值保存进去
public void set(T value) {}

//后面不再使用了调用,用于将变量销毁
public void remove();

//静态方法,提供一个函数式编程方法,用于返回一个设置了初始值的ThreadLocal变量
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {}

内部类

//静态内部类,ThreadLocalMap是实现ThreadLocal效果的数据结构工具类,可以看到它并没有实现Java的Map接口,而是自己实现了一套map。也是本次讲的源码的核心
static class ThreadLocalMap{
	
    //内部数组,以后每次扩容都是乘以2,容量始终是2的幂次
    private Entry[] table;
    
    //数组初始大小16
    private static final int INITIAL_CAPACITY = 16;
    
    //数组里Entry对象的个数
    private int size=0;
    
    //整理及扩容的阈值,结合下面的set方法来看,大小是数组容量的2/3
    private int threshold; // Default to 0
    
    private void setThreshold(int len) {
        threshold = len * 2 / 3;
    }
    
    //ThreadLocalMap内部类,和HashMap类似,Entry对象是内部数组对象的成员
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;
		
        //可以看到Entry对象的key是ThreadLocal对象,并且被保存为弱引用对象里
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
}

//上面静态方法:withInitial里用到的类,实际上是一个包装类,封装函数式编程接口的ThreadLocal子类,不重要
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
}

3.源码精读

下面我们开始进行源码详细解读,展开方式主要是get(),set(),remove()方法。

由于各个地方都用到了ThreadLocalMap,我先说一下我对ThreadLocalMap类的看法,首先ThreadLocalMap类是ThreadLocal的内部类,它是用来存储ThreadLocal对象和保存的变量值的映射关系的,数据结构及功能代码都封装在ThreadLocal文件中,但是在使用中主要作为Thread类的成员变量的。这一点是要有清晰认识的,试想一下,如果我们自己来实现ThreadLocal效果(线程隔离,封闭),我们会怎么去组织Thread和ThreadLocal的关系,我当初想当然的以为它里面应该保存线程和变量的映射关系,看来还是太年轻了呀!ThreadLocalMap作为Thread的成员,ThreadLocal作为ThreadLocalMap的key,于是Thread和TheadLocal就这样关联了起来,而且,这种实现方式没有任何内部对象逸出,线程封闭堪称完美。这种设计是值得我们学习的。

3.1 get()

public T get() {
    //获取当前线程
    Thread t = Thread.currentThread();
    //获取当前线程的ThreadLocalMap成员
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //由于Entry的key就是ThreadLocal对象自己,所以get方法不需要参数,getEntry的实现见下面
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    //如果没有获取到,就set并返回初始值
    return setInitialValue();
}
setInitialValue()
private T setInitialValue() {
	//获取初始值(默认是null),可以重写这个方法设置初始值(内部类:SuppliedThreadLocal就是这么干的)
    T value = initialValue();
    //获取当前线程
    Thread t = Thread.currentThread();
    //获取当前线程的ThreadLocalMap成员
    ThreadLocalMap map = getMap(t);
    if (map != null)
        //绑定当前ThreadLocal对象和初始值,set方法我们后面再说
        map.set(this, value);
    else
        //如果Thread的ThreadLocalMap成员为空,则new一个ThreadLocalMap赋值给t.threadLocals,ThreadLocalMap的数组初始容量是16,阈值16*2/3=10,同时将ThreadLocal对象和初始值保存进去。
        createMap(t, value);
    return value;
}
getEntry(ThreadLocal<?> key)
private Entry getEntry(ThreadLocal<?> key) {
    //根据ThreadLocal对象的成员threadLocalHashCode(魔数)和数组大小求与进行散列(效果等价于n%len求余,效率更高)。这个散列的精妙之处我放在最后和魔数一起讲
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key)
        //entry不是空槽,并且key和要找的ThreadLocal对象一样,则返回
        return e;
    else
        //顾名思义,在找不到Entry的时候返回一个entry
        return getEntryAfterMiss(key, i, e);
}
getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e)
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
    Entry[] tab = table;
    int len = tab.length;

    while (e != null) {
        ThreadLocal<?> k = e.get();
        if (k == key)
            //最后找到的话就返回
            return e;
        if (k == null)
            //e!=null&&k==null 说明key被gc了(key是弱引用,一定会在gc中被回收)
            //清除老旧对象(被gc过后,key==null的那种)expungeStaleEntry源码解读见下面
            expungeStaleEntry(i);
        else
            //e!=null&&k!=null&&k!=key 说明hash之后的槽位不是想要的对象,发生了碰撞,由于ThreadLocalMap中table数组处理冲突的方式是线性探测法(这个set的时候讲),所以继续往后找就对了
            i = nextIndex(i, len);
        e = tab[i];
    }
    //如果e==null
    return null;
}
nextIndex(int i, int len)和prevIndex(int i, int len)
//这里把nextIndex和prevIndex一起说是因为,这个两个方法在很多地方都有调用,是一个基础函数,index在达到数组索引最大值时会从0开始,同理,index在小于0时会从数组索引最大端开始
/**
 * Increment i modulo len. 等价于加1后对长度求余,保证返回的index始终在[0,len-1]之间
 */
private static int nextIndex(int i, int len) {
    return ((i + 1 < len) ? i + 1 : 0);
}

/**
 * Decrement i modulo len. 等价于减1后对长度求和再求余,保证返回的index始终在[0,len-1]之间
 */
private static int prevIndex(int i, int len) {
    return ((i - 1 >= 0) ? i - 1 : len - 1);
}
expungeStaleEntry(int staleSlot)

这个方法极为重要,主要是将一段连续非空的槽中的过期entry进行移除,并将遇到的正常的entry散列一次后(发生冲突时进行线性探测)直到放到最近的一个null位置上。staleSlot参数是一个已经过期的槽的index。expungeStaleEntry在后面的源码介绍中可以看到:它在几乎所有重要方法(get,set,remove)中都有调用。

private int expungeStaleEntry(int staleSlot) {
    Entry[] tab = table;
    int len = tab.length;

    // expunge entry at staleSlot
    //参数staleSlot是已经发现的过期的槽index,将发现的槽位清理处理出来,并帮助回收
    tab[staleSlot].value = null;	//将过期的槽位的value置为null,help gc 回收value对象
    tab[staleSlot] = null;			//将整个槽位置为空,help gc 回收整个entry对象
    size--;							//entry数组有效元素个数减一

    // 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 {
            //对槽位上的key(threadLocal对象)进行散列
            int h = k.threadLocalHashCode & (len - 1);
            if (h != i) {
                //如果槽位上的key散列出来的h不等于当前发现它的当前位置i,说明在之前进行过线性探测,需要重新进行线性探测,如果在[h,i)之间,遇到的第一个null槽位就会放进去(如果[h,i)之间真的没有空位,那它就还在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;
            }
        }
    }
    //返回的i位置上一定是null
    return i;
}

至此get所有源码解读完了。

3.2 set(T value)

//这个源码和上面已经解读过的setInitialValue()源码极为相识,除了没有设置初始值。没啥说的了
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        //这个set方法的解读见下文
        map.set(this, value);
    else
        createMap(t, value);
}
set(ThreadLocal<?> key, Object value)
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;
    int i = key.threadLocalHashCode & (len-1);

    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();

        if (k == key) {
            //找到了entry,设置value
            e.value = value;
            return;
        }

        if (k == null) {
            //e!=null&&k==null 说明是垃圾,使用replaceStaleEntry方法进行清理垃圾并将entry set到这个槽位,i表示待清理槽位index
            replaceStaleEntry(key, value, i);
            return;
        }
    }
	//能走到这里说明table数组中既没有找到匹配的key,也没有找到空位,而循环退出时,i所在槽位一定是null(原因是:循环是在tab[i]==null时退出的)
    tab[i] = new Entry(key, value);
    int sz = ++size;
    //cleanSomeSlots方法用来清理一部分垃圾,腾出空间(源码见下文)
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
        //cleanSomeSlots返回false表示:没有任何垃圾被清理,也没有空间被回收。并且当前table数组元素个数大于等于扩容阈值就会进行rehash(rehash详情见下文)
        rehash();
}
replaceStaleEntry(ThreadLocal<?> key, Object value,int staleSlot)
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).
    //记录当前垃圾槽位index
    int slotToExpunge = staleSlot;
    
    for (int i = prevIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = prevIndex(i, len))
        if (e.get() == null)
            //向左找连续的非空槽位,记录最左边的垃圾槽位到slotToExpunge(也可能达到最小值后跑到了右边)
            slotToExpunge = i;

    // Find either the key or trailing null slot of run, whichever
    // occurs first
    //向右找是否有可以set的key,以及是否有垃圾
    for (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.
        if (k == key) {
            //如果找到了要set的key,设置value
            e.value = value;

            tab[i] = tab[staleSlot];//将垃圾槽位的entry赋值给当前i槽位
            tab[staleSlot] = e;		//将发现的entry放入这个垃圾槽位

            // Start expunge at preceding stale entry if it exists
            //如果slotToExpunge == staleSlot说明最上面的那个for循环往前没有找到垃圾槽位
            if (slotToExpunge == staleSlot)
                //因为当前的这个垃圾槽位已经赋值给了i槽位,所以保留i到slotToExpunge
                slotToExpunge = i;
            //上面说过expungeStaleEntry是用来清理垃圾,它返回的index上一定是null,而cleanSomeSlots需要接收一个null的位置,并进行部分垃圾回收,源码见下面
            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.
        if (k == null && slotToExpunge == staleSlot)
            //往左没找到垃圾槽位,并且当前槽位是垃圾,则记录这个index,这个代码最多只会进入一次,也就是说:如果左边没有发现垃圾,则记录右边发现的第一个垃圾槽位
            slotToExpunge = i;
    }

    // If key not found, put new entry in stale slot
    //能走到这里,说明没有发现要可以set的key,将垃圾槽位置空,help gc
    tab[staleSlot].value = null;
    //新建一个entry,占据垃圾槽位
    tab[staleSlot] = new Entry(key, value);

    // If there are any other stale entries in run, expunge them
    //如果在staleSlot位置的两边任意一边发现垃圾,则进行垃圾清理,并重新hash垃圾右边的还在使用的对象
    if (slotToExpunge != staleSlot)
        cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}
cleanSomeSlots(int i, int n)
/**
 * Heuristically scan some cells looking for stale entries.
 * This is invoked when either a new element is added, or
 * another stale one has been expunged. It performs a
 * logarithmic number of scans, as a balance between no
 * scanning (fast but retains garbage) and a number of scans
 * proportional to number of elements, that would find all
 * garbage but would cause some insertions to take O(n) time.
 *
 * @param i a position known NOT to hold a stale entry. The
 * scan starts at the element after i.
 *
 * @param n scan control: {@code log2(n)} cells are scanned,
 * unless a stale entry is found, in which case
 * {@code log2(table.length)-1} additional cells are scanned.
 * When called from insertions, this parameter is the number
 * of elements, but when from replaceStaleEntry, it is the
 * table length. (Note: all this could be changed to be either
 * more or less aggressive by weighting n instead of just
 * using straight log n. But this version is simple, fast, and
 * seems to work well.)
 *
 * @return true if any stale entries have been removed.
 */	
//这个方法是部分清理槽位的,是专门设计的一种算法,是不扫描算法{优点:快,缺点:有垃圾}和table全量扫描算法{优点:能清理所有垃圾,缺点:全表扫性能为O(n)}在垃圾清理和性能上的一个折中,但是由于未全部扫描所以只能清理部分垃圾,如果发现了垃圾则n=len,并进行垃圾清理,并加多扫描的次数。它是一个自适应的算法,扫描到的垃圾越多,扫描的范围越大
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,代表循环的次数会变多
            n = len;
            removed = true;	//表示发现了垃圾
            i = expungeStaleEntry(i);//调用方法后,旧i到返回的新i之间的槽位都是清理过的
        }
    } while ( (n >>>= 1) != 0);	//'>>>'代表无符号右移,n>>>1实际上等价于n=n/2 
    return removed;	//发现过垃圾,并清理过返回true,未扫描到垃圾返回false
}
rehash()
//上面的代码提到:当cleanSomeSlots没有扫描到垃圾。并且当前table数组元素个数大于等于扩容阈值就会进行rehash
private void rehash() {
    //这是类中唯一一个全表扫描并清理垃圾的函数,代码见下面
    expungeStaleEntries();

    // Use lower threshold for doubling to avoid hysteresis
    //如果全表清理垃圾后,table中的元素个数大于等于3/4的扩容阈值,进行扩容。经过公式代换以及代码验证,我可以肯定的说:当size>=1/2的table.length时进行扩容。因为,threshold等于2/3的table.length,而size>=3/4的threshold,考虑到除法计算结果取整,变量代换后只能说size约等于1/2table.length时是resize得临界值,具体的情形在最后用代码举例证明。
    if (size >= threshold - threshold / 4)
        resize();
}
expungeStaleEntries()
//全表扫描并清理垃圾,不解释
private void expungeStaleEntries() {
    Entry[] tab = table;
    int len = tab.length;
    for (int j = 0; j < len; j++) {
        Entry e = tab[j];
        if (e != null && e.get() == null)
            expungeStaleEntry(j);
    }
}
resize()
/**
 * Double the capacity of the table.
 */
//将table容量扩容一倍,并重新hash所有entry 代码比较简单
private void resize() {
    Entry[] oldTab = table;
    int oldLen = oldTab.length;
    int newLen = oldLen * 2;
    Entry[] newTab = new Entry[newLen];
    int count = 0;

    for (int j = 0; j < oldLen; ++j) {
        Entry e = oldTab[j];
        if (e != null) {
            ThreadLocal<?> k = e.get();
            if (k == null) {
                e.value = null; // Help the GC
            } else {
                int h = k.threadLocalHashCode & (newLen - 1);
                //如果发现冲突,向右进行线性探测,直到下一个为null的槽位
                while (newTab[h] != null)
                    h = nextIndex(h, newLen);
                newTab[h] = e;
                count++;
            }
        }
    }
	//设置新的扩容阈值
    setThreshold(newLen);
    size = count;
    table = newTab;
}

到此,set方法中涉及到的方法源码都一一解释了

3.3 remove()

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        //remove代码见下面
        m.remove(this);
}
remove(ThreadLocal<?> key)
private void remove(ThreadLocal<?> key) {
    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)]) {
        if (e.get() == key) {
            //调用
            e.clear();
            expungeStaleEntry(i);
            return;
        }
    }
}

remove的代码就这么多。到此源码解读完毕。

4. 一些常见问题

4.1 如何避免因entry key是弱引用被gc后导致的内存泄漏?

所有通过threadLocal.set设置的值,都要调用相同的threadLocal.remove移除掉,即使key已经为空,remove也会清理掉垃圾。编码习惯要好。

4.2 remove,get,set方法中都有清理垃圾的方法调用,我是不是就可以不用调用remove了?

现在唯一知道可以触发全表扫描清理的是rehash方法,但这个方法不能直接触发,需要容量不够时才会触发。除此之外,不要依赖别的方法调用顺带清理自己的垃圾。谁创建的ThreadLocal,谁清理。remove, get, set都不能做到完全清理所有垃圾,它们都只是清理自己’碰巧’遇到的垃圾。

5.最后一些比较有意思的东西

5.1 entry size在什么时候进行rehash?rehash就一定会扩容吗?

通过上面的源码分析可以看出来:在set()中 未找到可以替换的旧对象,未扫描并清理出空闲空间,以及size>=length*2/3, 三个条件同时成立才会触发rehash。

rehash后就一定会扩容吗?不一定,因为,rehash第一步是要做全表扫描垃圾清理,清理了垃圾size应该会变小,如果清理完后size>=length*1/2,那么这个时候才会进行扩容。

为什么是length*1/2呢?我写了个代码进行了证明:

//length取值为:16,32,64,128。。。。
public static void main(String[] args){
    int length=16;
    while(length>0){
        int threshold=length*2/3;
        int size=threshold-threshold/4;
        System.out.println("length="+length+",size="+size);
        length<<=1;	//每次扩大1倍
    }
}

输出如下:

length=16,size=8
length=32,size=16
length=64,size=32
length=128,size=64
length=256,size=128
length=512,size=256
length=1024,size=512
length=2048,size=1024
length=4096,size=2048
length=8192,size=4096
length=16384,size=8192
length=32768,size=16384
length=65536,size=32768
length=131072,size=65536
length=262144,size=131072
length=524288,size=262144
length=1048576,size=524288
length=2097152,size=1048576
length=4194304,size=2097152
length=8388608,size=4194304
length=16777216,size=8388608
length=33554432,size=16777216
length=67108864,size=33554432
length=134217728,size=67108864
length=268435456,size=134217728
length=536870912,size=268435456
length=1073741824,size=-536870912

使用穷举法把所有的size临界值算了出来,所有的临界值都是length的一半,证明了最后扩容是size>=length/2,不用记很长一段公式了。

4.2 那个魔数HASH_INCREMENT = 0x61c88647,以及nextHashCode.getAndAdd(HASH_INCREMENT)获得的魔数是个什么鬼?

经查阅资料了解到,HASH_INCREMENT = 0x61c88647(1640531527)这个魔数来源是:2^32 * (√5-1)/2。 而(√5-1)/2又被成为黄金分割点,约等于0.618。2^32 * (√5-1)/2= 2654435769(无符号数)or -1640531527(有符号数)。 为什么不用2654435769呢,因为integer最大也才2147483647, 2654435769越界了啊。于是采用了有符号数的数字部分。

看源码知道,每new一个ThreadLocal对象,它的threadLocalHashCode,都是一个调用nextHashCode.getAndAdd(HASH_INCREMENT)之后生成的魔数,它们几乎各不相同。这个数以及nextHashCode.getAndAdd(HASH_INCREMENT)生成的后续魔数有多神奇呢?

我们做个试验。

private void hashcode(int n){
    AtomicInteger nextHashCode=new AtomicInteger();
    int HASH_INCREMENT=0x61c88647;  //1640531527
    TreeSet<Integer> set = new TreeSet<>();
    for(int i=0;i<n;i++){
        int result=nextHashCode.getAndAdd(HASH_INCREMENT);
        set.add(result&(n-1));
        System.out.print((result&(n-1))+" ");
    }
    System.out.println();
    System.out.println(set);
}

public static void main(String[] args){
    hashcode(16);
    hashcode(32);
    hashcode(64);
    hashcode(128);
    hashcode(256);
    hashcode(512);
    hashcode(1024);
    hashcode(2048);
    hashcode(4096);
    hashcode(8192);
}

结果是:(下面只截取了16~512的结果,太大了,读者可以自行试验)

0 7 14 5 12 3 10 1 8 15 6 13 4 11 2 9 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
0 7 14 21 28 3 10 17 24 31 6 13 20 27 2 9 16 23 30 5 12 19 26 1 8 15 22 29 4 11 18 25 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
0 7 14 21 28 35 42 49 56 63 6 13 20 27 34 41 48 55 62 5 12 19 26 33 40 47 54 61 4 11 18 25 32 39 46 53 60 3 10 17 24 31 38 45 52 59 2 9 16 23 30 37 44 51 58 1 8 15 22 29 36 43 50 57 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63]
0 71 14 85 28 99 42 113 56 127 70 13 84 27 98 41 112 55 126 69 12 83 26 97 40 111 54 125 68 11 82 25 96 39 110 53 124 67 10 81 24 95 38 109 52 123 66 9 80 23 94 37 108 51 122 65 8 79 22 93 36 107 50 121 64 7 78 21 92 35 106 49 120 63 6 77 20 91 34 105 48 119 62 5 76 19 90 33 104 47 118 61 4 75 18 89 32 103 46 117 60 3 74 17 88 31 102 45 116 59 2 73 16 87 30 101 44 115 58 1 72 15 86 29 100 43 114 57 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127]
0 71 142 213 28 99 170 241 56 127 198 13 84 155 226 41 112 183 254 69 140 211 26 97 168 239 54 125 196 11 82 153 224 39 110 181 252 67 138 209 24 95 166 237 52 123 194 9 80 151 222 37 108 179 250 65 136 207 22 93 164 235 50 121 192 7 78 149 220 35 106 177 248 63 134 205 20 91 162 233 48 119 190 5 76 147 218 33 104 175 246 61 132 203 18 89 160 231 46 117 188 3 74 145 216 31 102 173 244 59 130 201 16 87 158 229 44 115 186 1 72 143 214 29 100 171 242 57 128 199 14 85 156 227 42 113 184 255 70 141 212 27 98 169 240 55 126 197 12 83 154 225 40 111 182 253 68 139 210 25 96 167 238 53 124 195 10 81 152 223 38 109 180 251 66 137 208 23 94 165 236 51 122 193 8 79 150 221 36 107 178 249 64 135 206 21 92 163 234 49 120 191 6 77 148 219 34 105 176 247 62 133 204 19 90 161 232 47 118 189 4 75 146 217 32 103 174 245 60 131 202 17 88 159 230 45 116 187 2 73 144 215 30 101 172 243 58 129 200 15 86 157 228 43 114 185 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255]
0 71 142 213 284 355 426 497 56 127 198 269 340 411 482 41 112 183 254 325 396 467 26 97 168 239 310 381 452 11 82 153 224 295 366 437 508 67 138 209 280 351 422 493 52 123 194 265 336 407 478 37 108 179 250 321 392 463 22 93 164 235 306 377 448 7 78 149 220 291 362 433 504 63 134 205 276 347 418 489 48 119 190 261 332 403 474 33 104 175 246 317 388 459 18 89 160 231 302 373 444 3 74 145 216 287 358 429 500 59 130 201 272 343 414 485 44 115 186 257 328 399 470 29 100 171 242 313 384 455 14 85 156 227 298 369 440 511 70 141 212 283 354 425 496 55 126 197 268 339 410 481 40 111 182 253 324 395 466 25 96 167 238 309 380 451 10 81 152 223 294 365 436 507 66 137 208 279 350 421 492 51 122 193 264 335 406 477 36 107 178 249 320 391 462 21 92 163 234 305 376 447 6 77 148 219 290 361 432 503 62 133 204 275 346 417 488 47 118 189 260 331 402 473 32 103 174 245 316 387 458 17 88 159 230 301 372 443 2 73 144 215 286 357 428 499 58 129 200 271 342 413 484 43 114 185 256 327 398 469 28 99 170 241 312 383 454 13 84 155 226 297 368 439 510 69 140 211 282 353 424 495 54 125 196 267 338 409 480 39 110 181 252 323 394 465 24 95 166 237 308 379 450 9 80 151 222 293 364 435 506 65 136 207 278 349 420 491 50 121 192 263 334 405 476 35 106 177 248 319 390 461 20 91 162 233 304 375 446 5 76 147 218 289 360 431 502 61 132 203 274 345 416 487 46 117 188 259 330 401 472 31 102 173 244 315 386 457 16 87 158 229 300 371 442 1 72 143 214 285 356 427 498 57 128 199 270 341 412 483 42 113 184 255 326 397 468 27 98 169 240 311 382 453 12 83 154 225 296 367 438 509 68 139 210 281 352 423 494 53 124 195 266 337 408 479 38 109 180 251 322 393 464 23 94 165 236 307 378 449 8 79 150 221 292 363 434 505 64 135 206 277 348 419 490 49 120 191 262 333 404 475 34 105 176 247 318 389 460 19 90 161 232 303 374 445 4 75 146 217 288 359 430 501 60 131 202 273 344 415 486 45 116 187 258 329 400 471 30 101 172 243 314 385 456 15 86 157 228 299 370 441 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511]

奇数行是这一系列魔数对 2的各个幂数散列后的结果,偶数行是我对散列后的数据进行排序后的结果,发现没?完美散列,没有冲突!!!不信你可以再往下试验,试验更大的2的幂数

看到这个结果,我感觉我被雷的外焦里嫩,神奇的黄金分割点,神奇的散列!!!以前只知道黄金分割比例的事物是有一种自然美的,2^32*黄金分割点居然能完美散列2的幂数????!!!!给跪了

也就是说,每个加入到容量刚好是2的幂数的ThreadLocalMap table中并进行散列存储的ThreadLocal对象,基本可以做到完美散列在table中!!!!!

除此之外,还发现了一个有意思的东西,每个ThreadLocal的threadLocalHashCode基本都各不相同,但我知道threadLocalHashCode的生成规则,就能推算出后续的所有魔数结果,那么你给我一个ThreadLocal对象,我甚至可以告诉你,这个对象是jvm启动以来,第几个创建的ThreadLocal对象。换句话说,如果这个ThreadLocal用来做用户会话管理,拿到一个会话的ThreadLocal对象,我能知道当前JVM系统从启动以来已经创建了多少个会话了。

万万没想到,源码中的一些魔数能挖出这么多东西!

至此源码解读完毕,水平有限,欢迎大家批评指正!有问题也可以留言讨论。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocal是Java中的一个线程本地变量,它提供了一种线程安全的方式来存储每个线程的局部变量。ThreadLocal的实现原理是在每个线程中创建一个独立的副本,每个线程都可以访问自己的副本,从而避免了线程安全问题。 ThreadLocal的内部结构主要包括ThreadLocal类和ThreadLocalMap类。ThreadLocal类是一个泛型类,用于存储线程本地变量的值。ThreadLocalMap类是一个自定义的哈希表,用于存储每个线程的ThreadLocal变量副本。 在jdk8之前,ThreadLocalMap是通过自定义的Entry数组来实现的,每个Entry包含一个ThreadLocal对象和对应的值。在jdk8之后,ThreadLocalMap的实现方式发生了变化,它使用了类似于HashMap的Node数组来存储Entry,从而提高了性能。 ThreadLocal内存泄漏问题是指在使用ThreadLocal时,由于没有及时清理ThreadLocal变量,导致线程结束后ThreadLocal变量没有被回收,从而导致内存泄漏。解决ThreadLocal内存泄漏问题的方法是在使用完ThreadLocal变量后,调用remove()方法将其从ThreadLocalMap中删除。 下面是一个简单的示例,演示了如何使用ThreadLocal来存储线程本地变量: ```java public class ThreadLocalDemo { private static final ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { Thread t1 = new Thread(() -> { threadLocal.set("Hello from thread 1"); System.out.println(threadLocal.get()); }); Thread t2 = new Thread(() -> { threadLocal.set("Hello from thread 2"); System.out.println(threadLocal.get()); }); t1.start(); t2.start(); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值