ThreadLocal的源码探究

ThreadLocal的实现原理就是ThreadLocal里面有一个静态的ThreadLocalMap,这个是怎么操作的呢,往下看

ThreadLocalMap是ThreadLocal里面的一个静态类
源码如下所示
ThreadLocal的源码探究_第1张图片

ThreadLocalMap是Thread里面的一个属性

为什么这样说呢,首先我们看到Thread类里面有一个属性就叫做ThreadLocalMap
ThreadLocal的源码探究_第2张图片

然后我们看一下ThreadLocal里面的set()方法,看到源码可以看到当当前线程的ThreadLocalMap是null的时候,此时就会走下面的红色框里面的代码
ThreadLocal的源码探究_第3张图片

然后就是会走下面的方法,然后new一个ThreadLocalMap赋值给当前线程的threadLocals这个变量,threadLocals这个变量在Thread里面就是ThreadLocal.ThreadLocalMap类型的
在这里插入图片描述

然后当set()的时候,会把当前实例化对象的ThreadLocal作为key值,然后把set()里面的参数当成value放到这个ThreadLocalMap里面

在ThreadLocal里面源码set(),此时可以看到set()的源码就是首先获取当前线程,然后利用当前线程获取一个ThreadLocalMap的对象,然后如果ThreadLocalMap对象为空,则把当前ThreadLocal当成key,set()里面的参数当成value放到ThreadLocalMap集合里面,否则创建这个ThreadLocalMap对象,然后把当前ThreadLocal当成key,set()里面的参数当成value放到ThreadLocalMap集合里面

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

上面的createMap(t,value)源码如下

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

看到上面的代码之后,我们也能知道为什么ThreadLocal可以把创建的变量只能被当前线程访问,其他线程则无法访问和修改因为ThreadLocal的值是放入了当前线程的一个ThreadLocalMap实例中,所以只能在本线程中访问,其他线程无法访问。

然后在jdk1.8里面,发现ThreadLocalMap里面是用Entry[]这个数组来实现ThreadLocalMap的功能

使用过程是:Entry类里面的key值就是ThreadLocal,并且是弱引用,然后Entry类里面的value属性对应放入到ThreadLocalMap调用set方法里面的第二个参数
看代码:下面是 jdk1.8里面的ThreadLocalMap类里面的set方法,然后下面的蓝色代码里面的Entry类

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();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

Entry类就是指ThreadLocalMap类里面的Entry类,如下所示

ThreadLocal的源码探究_第4张图片
然后我们看一下ThreadLocalMap里面的set()方法是怎么进行存储的

首先可以看到下面的红色代码里面的threadLocalHashCode就是获取当前ThreadLocal实例对象的threadLocalHashCode属性,这个threadLocalHashCode属性就是唯一的,它是怎么来的呢,看下面的图片,首先是ThreadLocal类里面的HASH_INCREMENT常量,然后通过下面的nextHashCode()方法来进行原子性的增加1,然后把nextHashCode()方法的返回值给当前的ThreadLocal实例化对象的threadLocalHashCode属性,这样就保证唯一性了
ThreadLocal的源码探究_第5张图片
然后把这个ThreadLocal的threadLocalHashCode值当成ThreadLocalMap类里面的Entry数组的索引值,就是下面的绿色代码,这样就把ThreadLocal调用的set()方法的两个参数都存储到ThreadLocalMap里面的Entry数组里面了

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();

                if (k == key) {
                    e.value = value;
                    return;
                }

                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            // 绿色代码开始
            tab[i] = new Entry(key, value);
            // 绿色代码结束
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

能看到这里的同学,就帮忙点个赞吧,Thanks♪(・ω・)ノ

你可能感兴趣的:(#,Java类,#,Java多线程)