ThreadLocal原理解析(一)之---基础属性

最近在学习ThreadLocal相关知识,进行了下记录。
一:ThreadLocal是什么?
ThreadLocal是服务于Thread的一种本地私有数据机制,threadlocalvariable(线程局部变量), 即为每一个使用该变量的线程都提供一个变量值的副本,与使用的线程绑定,每一个线程都可以独立地改变自己的副本,不会产生冲突。 其在Thread处于活跃状态,并且threadLocal对象可用的情况下,就可以在其中存放数据。在线程被销毁之后,则ThreadLocal中使用的资源和内存同时也被回收。本质上,JVM通过ThreadLocal为多线程情况下,进行资源的访问提供了一种隔离机制,简化了编程的复杂度。
二:ThreadLocal的实现原理是什么?
每一个线程都有一个对应的Thread对象,而Thread类有一个成员变量,它是一个Map集合,这个Map集合的key就是ThreadLocal的引用,而value就是当前线程在key所对应的ThreadLocal中存储的值。当某个线程需要获取存储在ThreadLocal变量中的值时,ThreadLocal底层会获取当前线程的Thread对象中的Map集合,然后以ThreadLocal作为key,从Map集合中查找value值。这就是ThreadLocal实现线程独立的原理。也就是说,ThreadLocal能够做到线程独立,是因为值并不存在ThreadLocal中,而是存储在线程对象中。下面我们根据ThreadLocal中两个最重要的方法来确认这一点。
在这里插入图片描述
三:接下来就先了解下ThreadLocal的相关属性
3.1 threadLocalHashCode:该字段的含义是:
线程获取threadLocal.get()时, 如果第一次在某个 threadLocal对象上get时,会给当前线程分配一个value, 如果这个value和当前的threadLocal对象 被包装成一个entry 其中 key 是 threadLocal对象,value是threadLocal对象给当前线程生成的value.
这个entry 存放到当前线程 threadLocals 这个map 的哪个桶位?
于当前threadLocal对象的threadLocalHashCode有关系
使用threadLocalHashCode & (table.length-1)得到的位置,就是当前entry需要存放的位置

    private final int threadLocalHashCode = nextHashCode();

3.2 nextHashCode:创建threadLocal对象时,每创建一个threadLocal对象 就会使用nextHashCode 分配一个hash值给这个对象

    private static AtomicInteger nextHashCode =
            new AtomicInteger();

3.3 HASH_INCREMENT: 每创建一个threadLocal对象,这个ThreadLocal.nextHashCode 这个值就会增长 0x61c88647。这个值就是 斐波那契数。

 private static final int HASH_INCREMENT = 0x61c88647;

3.4 nextHashCode()方法: 创建新的ThreadLocal对象时,会给当前对象分配一个hash.使用的就是这个方法

    private static int nextHashCode() {
    
    
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

3.5 initialValue()方法:默认返回null,一般需要重写

  protected T initialValue() {
    
    
        return null;
    }

3.6 接下来就是比较重要的三个方法之一get()方法:get方法的作用是:
返回当前线程于当前ThreadLocal对象相关联的 线程局部变量,这个变量只有当前线程能访问到。
如果当前线程没有分配,则给当前线程去分配(使用initialValue()方法)
在get()方法中, 先获取当前线程,在通过getMap()方法获取threadLocals map引用 。
如果条件成立,说明当前线程已经拥有了自己的ThreadLocalMap对象, 同时entry如有有值,表示当前线程已经初始化过了,于当前线程threadLocal对象关联的 线程局部变量,并返回线程的value值
如果map活着entry为null,则会进行初始化.

    // 返回当前线程于当前ThreadLocal对象相关联的 线程局部变量,这个变量只有当前线程能访问到。
    // 如果当前线程没有分配,则给当前线程去分配(使用initialValue()方法)
    public T get() {
    
    
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取当前线程Thread对象的 threadLocals map 引用
        ThreadLocalMap map = getMap(t);
        // 如果条件成立,说明当前线程已经拥有自己的 ThreadLocalMap 对象了
        if (map != null) {
    
    
            // 可以:当前threadLocal对象
            // 调用map.getEntry()方法,获取threadLocalMap中 该threadLocal 关联的 entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            // 条件成立,当前线程初始化过 与当前线程threadLocal对象相关联的 线程局部变量
            if (e != null) {
    
    
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                // 返回value值
                return result;
            }
        }

        // 执行到这里,map是null 或者 entry是 null

        // 初始化当前线程与当前threadLocal对象 相关联的value
        // 且当前线程如果没有threadLocalMap的话,还会初始化创建map
        return setInitialValue();
    }

在这个方法中,有两个小方法;getMap()方法和setInitialValue()方法
3.7 getMap()方法: 主要作用是返回当前线程的 threadLocals

    ThreadLocalMap getMap(Thread t) {
    
    
        // 返回当前线程的 threadLocals
        return t.threadLocals;
    }

3.8setInitialValue()方法:进行当前线程的初始化
先调用initalValue()方法进行初始化,获取当前线程。进行调用getMap()方法
如果map有值,会保存当前threadLocal与当前线程相关联的value。这个map的key 就是当前线程的threadLocal对象.value就是 线程与当前threadLocal相关的局部变量
如果map为null ,会进行创建。传入的k为当前线程和当前线程的value值
最后返回线程与当前threadLocal的局部变量

    private T setInitialValue() {
    
    
        // 调用当前ThreadLocal对象的initialValue方法
        // value 就是当前ThreadLocal对象与当前线程生成的线程的局部变量
        T value = initialValue();
        //获取当前线程
        Thread t = Thread.currentThread();
        // 获取当前线程内部的threadLocals threadLocalMap对象
        ThreadLocalMap map = getMap(t);
        // 条件成立,说明当前线程内部初始化过threadLocalMap对象了(线程的threadLocals 只会初始化一次)
        if (map != null)
            // 保存当前threadLocal与当前线程相关联的 value
            // key:当前threadLocal对象
            // value:线程于当前threadLocal相关的局部变量
            map.set(this, value);
        else
            // 不成立的话,说明当前线程内部还未初始化
            // 参数一:当前线程; 参数二:当前线程的value值
            createMap(t, value);

        //返回线程于当前threadLocal的局部变量
        return value;
    }

这这个方法中,有个新的方法createMap():方法
3.9.createMap()方法:进行创建一个新的threadLocalMap

    void createMap(Thread t, T firstValue) {
    
    
        // 传递t 的意义就是 要访问 当前这个线程 t.threadLocals字段,给这个字段初始化

        // new ThreadLocalMap(this,firstValue) 创建一个ThreadLocalMap对象
        // 初始化 key: this当前threadLocal对象
        // value: 线程于当前threadLocal相关的局部变量
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

3.11接下来一个方法,也是ThreadLocal中最重要的三个方法之一 set()方法
主要作用是:修改当前线程与当前threadLocal对象相关的局部变量
通过先获取当前线程,再获取ThreadLocalMap ,判断map是否存在。如果map不为null,表示当前线程已经初始化了,只需要map.set即可,进行重写或者添加。
如果map为null,说明还未创建,需要调用createMap()进行初始化的创建

 // 修改当前线程于当前threadLocal对象相关联的 线程局部变量
    public void set(T value) {
    
    
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取当前线程threadLocalMap对象
        ThreadLocalMap map = getMap(t);
        // 条件成立, 说明当前线程的threadLocal已经初始化过了
        if (map != null)
            // 调用threadLocalMap.set方法,进行重写或者添加
            map.set(this, value);
        else
            // 执行到这,说明的哪个区线程还未创建
            createMap(t, value);
    }

3.12第三个重要的方法就是remove()方法: 顾名思义就是移除
移除当前线程于当前threadLocal对象相关联的 线程局部变量
获取当前的线程的map,如果存在的话,就进行remove移除

    // 移除当前线程于当前threadLocal对象相关联的 线程局部变量
    public void remove() {
    
    
        // 获取当前线程的map
        ThreadLocalMap m = getMap(Thread.currentThread());
        // 如果成立,直接remove
        if (m != null)
            m.remove(this);
    }

おすすめ

転載: blog.csdn.net/qq_35529931/article/details/120244273