android-----ThreadLocal源码分析

        今天在复习Handler消息处理机制原理的时候,发现自己对android的ThreadLocal部分理解还不是很到位,在此做个总结,先来说说为什么会在Handler消息处理机制中出现ThreadLocal这个东西吧,我们都知道Handler发送消息到MessageQueue中,Looper从MessageQueue中取出消息来执行,但是Looper呢只是一个封装类而已,虽然它里面有loop方法,但是真正的运作还是需要线程的,那么Looper是怎么和线程联系到一起的呢?就是采用ThreadLocal了,之前我分析过java中的ThreadLocal源码,参见:java-----ThreadLocal源码分析,这篇我们分析的是android中的ThreadLocal源码,大家都知道解决多线程并发问题我们可以采用synchronized关键字来实现,他依托的是JVM的锁机制来实现临界区的变量、函数在CPU运行中访问的原子性;而ThreadLocal则是在每个线程中各自都保留一个共享内容的副本,自己修改自己的当然不会出现问题了,也就是说两者还是有本质区别的,使用synchronized之后所有线程修改的是同一个内容,一个线程对其的修改是会影响到另一个线程的访问该共享内容初始值的,只不过是按顺序修改而已,而采用ThreadLocal的话一个线程修改的内容对另一个线程来说是并不可见的,android中的ThreadLocal在java原生ThreadLocal的基础上进行了一些 优化操作,个人认为最大的优化地方在于采用数组的方式存放ThreadLocal引用以及值而不是之前的map方式;

        和原生态的ThreadLocal一样,android中的ThreadLocal源码最重要的方法也是set和get方法,首先看看set方法:

 public void set(T value) {
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values == null) {
            values = initializeValues(currentThread);
        }
        values.put(this, value);
    }
        首先获取到当前正在执行的线程,并且通过values方法获取当前线程的Values值,其中Values是定义在ThreadLocal中的静态类,他的功能就是存储放进来的数据以及对数据进行处理,比如清理失效数据,扩展table数组等等,他是通过Object数组来进行存放数据的,及时清理无效的数据并且扩展或者缩小table的大小,便于保持当前table的健壮性,查看values方法:

 Values values(Thread current) {
        return current.localValues;
    }
        可以发现他仅仅是返回当前线程的localValues属性值而已,进入到Thread类中我们看到有如下代码:

  ThreadLocal.Values localValues;
        即localValues是Values类型的变量,回到set方法中,在通过values方法获得Values对象之后,判断该对象是否为空,如果为空的话,则执行initializeValues方法:

Values initializeValues(Thread current) {
        return current.localValues = new Values();
    }
        可以看到该方法仅仅只是创建一个Values对象出来,并且将其赋给当前线程的localValues属性;

        回到set方法中,最后我们执行了values的put方法:

void put(ThreadLocal<?> key, Object value) {
            cleanUp();

            // Keep track of first tombstone. That's where we want to go back
            // and add an entry if necessary.
            int firstTombstone = -1;

            for (int index = key.hash & mask;; index = next(index)) {
                Object k = table[index];

                if (k == key.reference) {
                    // Replace existing entry.
                    table[index + 1] = value;
                    return;
                }

                if (k == null) {
                    if (firstTombstone == -1) {
                        // Fill in null slot.
                        table[index] = key.reference;
                        table[index + 1] = value;
                        size++;
                        return;
                    }

                    // Go back and replace first tombstone.
                    table[firstTombstone] = key.reference;
                    table[firstTombstone + 1] = value;
                    tombstones--;
                    size++;
                    return;
                }

                // Remember first tombstone.
                if (firstTombstone == -1 && k == TOMBSTONE) {
                    firstTombstone = index;
                }
            }
        }

        在分析put方法之前我们需要了解一点,在ThreadLocal类中定义了一个Reference变量:

  private final Reference<ThreadLocal<T>> reference
            = new WeakReference<ThreadLocal<T>>(this);
        他是一个弱引用,引用了ThreadLocal实例自己;
        简单分析下put方法,从这段源码中我们看到了大部分的操作都是在修改table数组内容,他是一个Object类型的数组,定义在Values方法中,是他的一个属性,长度总是2乘方倍数,ThreadLocal以及其对应的值都是连续的存储在这个数组中的,具体来说ThreadLocal的reference值存储在其对应值的前一个位置处,通过key.hash & mask获取到当前ThreadLocal在table数组中的index值,关于key.hash & mask的奇妙之处就在于它能够保证获取到的index值不会越界同时呢也能保证对于不同的ThreadLocal得到的index值是不同的这一点,具体是通过斐波拉契散列寻址方式实现的,其中mask即计算下标的掩码,其值为table的长度减1,从上面的put方法中我们最能够直观的看出ThreadLocal的值在table数组中的存储位置为此ThreadLocal的reference字段所标识对象的下一个位置,比如:

 table[index] = key.reference;
 table[index + 1] = value;

那么index存储的就是当前ThreadLocal的reference弱引用,index+1存储的就是当前ThreadLocal的值;

        其实这点在Values的add方法中是最能直观的看出来的:

  void add(ThreadLocal<?> key, Object value) {
            for (int index = key.hash & mask;; index = next(index)) {
                Object k = table[index];
                if (k == null) {
                    table[index] = key.reference;
                    table[index + 1] = value;
                    return;
                }
            }
        }

        接下来看看他的get方法:

public T get() {
        // Optimized for the fast path.
        Thread currentThread = Thread.currentThread();
        Values values = values(currentThread);
        if (values != null) {
            Object[] table = values.table;
            int index = hash & values.mask;
            if (this.reference == table[index]) {
                return (T) table[index + 1];
            }
        } else {
            values = initializeValues(currentThread);
        }

        return (T) values.getAfterMiss(this);
    }
        同样首先获取当前正在执行的线程,接着获取当前线程的localValues属性值,这个和set方法是一样的,如果当前线程的localValues属性值不为空的话,则取出他的table数组,并且找出当前ThreadLocal对象的reference对应的index值,返回table数组中index+1位置上的值,也就是对应于当前ThreadLocal的值;如果当前线程的localValues属性值为空的话,则执行initializeValues方法,创建一个Values对象出来,并且通过values的getAfterMiss方法返回一个初始值对象,简单查看getAfterMiss源码之后发现这个初始值是通过下面代码获取的:

 Object value = key.initialValue();
        也就是调用的ThreadLocal的initialValue方法,这个方法是protected修饰的方法,我们也可以通过继承ThreadLocal类来重写这个方法,默认情况下是返回null的;

 protected T initialValue() {
        return null;
    }
        至此,android中的ThreadLocal主要源码分析完毕,下面做个小结:

        (1)android中的ThreadLocal值是存储在Values静态类中的table数组中的,这个数组中的0,2,4....2n下标存储的是ThreadLocal对应的reference值,而1,3,5....(2n+1)下标存储的是对应于reference的ThreadLocal值,某种意义上这里的table数组是类似于map方式的;

        (2)寻址的时候是采用斐波那契散列的方式获得index值的


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
FastThreadLocal 是 Netty 中的一个优化版 ThreadLocal 实现。与 JDK 自带的 ThreadLocal 相比,FastThreadLocal 在性能上有所提升。 FastThreadLocal 的性能优势主要体现在以下几个方面: 1. 线程安全性:FastThreadLocal 使用了一种高效的方式来保证线程安全,避免了使用锁的开销,使得在高并发场景下性能更好。 2. 内存占用:FastThreadLocal 的内部数据结构更加紧凑,占用的内存更少,减少了对堆内存的占用,提高了内存的利用效率。 3. 访问速度:FastThreadLocal 在访问时,使用了直接索引的方式,避免了哈希表查找的开销,使得访问速度更快。 在 Netty 源码中,FastThreadLocal 主要被用于优化线程的局部变量存储,提高线程之间的数据隔离性和访问效率。通过使用 FastThreadLocal,Netty 在高性能的网络通信中能够更好地管理线程的局部变量,提供更高的性能和并发能力。 引用中提到的代码片段展示了 Netty 中的 InternalThreadLocalMap 的获取方式。如果当前线程是 FastThreadLocalThread 类型的线程,那么就直接调用 fastGet 方法来获取 InternalThreadLocalMap 实例;否则,调用 slowGet 方法来获取。 fastGet 方法中,会先尝试获取线程的 threadLocalMap 属性,如果不存在则创建一个新的 InternalThreadLocalMap,并设置为线程的 threadLocalMap 属性。最后返回获取到的 threadLocalMap。 slowGet 方法中,通过调用 UnpaddedInternalThreadLocalMap.slowThreadLocalMap 的 get 方法来获取 InternalThreadLocalMap 实例。如果获取到的实例为 null,则创建一个新的 InternalThreadLocalMap,并将其设置到 slowThreadLocalMap 中。最后返回获取到的 InternalThreadLocalMap。 综上所述,FastThreadLocal 是 Netty 中为了优化线程局部变量存储而设计的一种高性能的 ThreadLocal 实现。它通过减少锁的开销、优化内存占用和加快访问速度来提升性能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [FastThreadLocal源码分析](https://blog.csdn.net/lvlei19911108/article/details/118021402)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [Netty 高性能之道 FastThreadLocal 源码分析(快且安全)](https://blog.csdn.net/weixin_33871366/article/details/94653953)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值