美文网首页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