美文网首页Android技术知识Android开发经验谈
Handler---4部曲---2. ThreadLocal 存

Handler---4部曲---2. ThreadLocal 存

作者: liys_android | 来源:发表于2021-11-06 23:39 被阅读0次

    Handler---4部曲---1. 总体流程
    Handler---4部曲---2. ThreadLocal 存储主流程
    Handler---4部曲---3. MessageQueue队列
    Handler---4部曲---4.细节补充

    前言

    看Handler源码, 每次一说到ThreadLocal的保存, 几乎都说可以理解成key就是当前线程, value就是对应的值. 实际上真的是这样吗?
    主要了解整个存储的流程,细节方面不做讲解.

    先上图
    ThreadLocal 类之间的关系.png ThreadLocal 存储流程图.png
    一. 先记住

    每个ThreadLocal对象中,threadLocalHashCode 的值是固定的

    //ThreadLocal类中
    final int threadLocalHashCode = nextHashCode();
    
    注意:
    1. table数组的角标 i 和 ThreadLocal.threadLocalHashCode 有关.
    2. key当前ThreadLocal对象
    3. value 就是存入的值
    二. ThreadLocal.set() 方法
        public void set(T value) {
            Thread t = Thread.currentThread();
            //从当前线程中, 获取成员变量 threadLocals
            ThreadLocalMap map = getMap(t);
            if (map != null)
                //保存数据
                map.set(this, value);
            else
                // 首次进入,创建一个ThreadLocalMap,并赋值给当前线程
                createMap(t, value);
        }
    
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    
        void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
    

    步骤:

    1. 从当前线程取出成员变量:ThreadLocalMap threadLocals;
    2. threadLocals为空, 创建ThreadLocalMap, 并赋值.
    3. threadLocals不为空,直接设置值
    4. 每个线程保存的本质, 就是在当前线程中的ThreadLocalMap对象中进行存储.
    三. 下面看看 new ThreadLocalMap() 和 ThreadLocalMap.set() 都做了些什么?

    3.1 ThreadLocalMap构造方法

            ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
                //初始化table数组, 数组里面是没有值的
                table = new Entry[INITIAL_CAPACITY];
                int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
                //保存对应的值
                table[i] = new Entry(firstKey, firstValue);
            }
    

    步骤:

    1. 初始化table数组---->没有赋值
    2. tab[i] 保存值

    3.2 ThreadLocalMap中的set方法

     private void set(ThreadLocal<?> key, Object value) {
                Entry[] tab = table;
                int len = tab.length;
                //tab的位置
                int i = key.threadLocalHashCode & (len-1);
                //Entry e = tab[i] --->当前 ThreadLocal 对应的值
                // 如果tab[i] 有值, 证明之前保存过
                for (Entry e = tab[i]; 
                        e != null; 
                        //这里更新了i,  i = i+1
                        e = tab[i = nextIndex(i, len)]) {
                    ThreadLocal<?> k = e.get();
                    //同一个ThreadLocal, 直接覆盖
                    if (k == key) {
                        e.value = value;
                        return;
                    }
                }
                //如果 tab[i] == null, 直接保存
                tab[i] = new Entry(key, value);
                int sz = ++size;
                //判断table数组是否需要扩容
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
            }
    

    步骤:

    1. 查看tab[i]是否有值?
    2. 如果有值,并且key也相同, 覆盖原来的值
    3. 如果没有值,直接赋值.
    4. 判断table数组 是否扩容.
    结论:
    1. 一个线程Thread,  只有一个ThreadLocalMap成员变量, 可以有多个ThreadLocal对象
    2. 同一个线程,多个ThreadLocal时, 存储的值在同一个Thread.ThreadLocalMap里面的  table数组.  类型Entry[]
    3. 不同线程, 同一个ThreadLocal时, 存储在不同的Thread对象中.    1个Thread 对应 1个 ThreadLocalMap
    

    到这里整个保存的流程就结束了

    相关文章

      网友评论

        本文标题:Handler---4部曲---2. ThreadLocal 存

        本文链接:https://www.haomeiwen.com/subject/drbrultx.html