这玩意比ThreadLocal叼多了,吓得why哥赶紧分享出来。 (4)

简介: 这玩意比ThreadLocal叼多了,吓得why哥赶紧分享出来。 (4)

标号为 ① 的地方就是往 InternalThreadLocalMap 这个数组中存放我们传进来的 value。

存的时候分为两种情况。


标号为 ② 的地方是数组容量还够,能放进去,那么可以直接设置。


标号为 ③ 的地方是数组容量不够用,需要扩容了。


这里抛出两个问题:


扩容是怎么扩的?


数组下标是怎么来的?


先看问题一,怎么扩容的?


看源码:



image.png


和 HashMap 里面的位运算异曲同工。


在 InternalThreadLocalMap 中扩容就是变成原来大小的 2 倍。从 32 到 64,从 64 到 128 这样。


扩容完成之后把原数组里面的值拷贝到新的数组里面去。


然后剩下的部分用 UNSET 填充。最后把我们传进来的 value 放到指定位置上。


再看看问题二,数组下标怎么来的?也就是这个 index:


微信图片_20220426232104.png


从上往下看,可以看到最后,这个 index 本质上是一个 AtomicInteger 。


主要看一下标号为 ① 的地方。


index 每次都是加一,对应的是 InternalThreadLocalMap 里的数组下标。


第一眼看到的时候,里面的 if 判断 index<0 我是可以理解的,防止溢出嘛。


但是下面在抛出异常之前,还调用了 decrementAndGet 方法,又把值减回去了。


你说这是为什么?


开始我没想明白。但是有天晚上睡觉之前,电光火石一瞬间我想明白了。


image.png


如果不把值减回去,加一的代码还在不断的被调用,那么这个 index 理论上讲是有可能又被加到正数的,这一点你能明白吧?


为什么我说理论上呢?


int 的取值范围是 [-2147483648 到 2147483647]。


如果 int 从 0 增加,一直溢出到 -2147483648,再从 -2147483648 加到 0,中间有 4294967295 个数字。


一个数字对应数组的一个下标,就算里面放的是一个字节的 boolean 型,那么大概也就是 4T 的内存吧。


所以,我觉得这是理论上的。


到这一步,我们已经完成了从 Thread 里面取出 InternalThreadLocalMap ,并且往里面放数据的操作。


最后,InternalThreadLocal 的 set 方法只剩下最后一行代码,我们还没说:



image.png


就是 setIndexedVariable 方法返回 true 后,会执行 addToVariablesToRemove 方法。


这个方法其实就是在数组的第一个位置维护当前线程里面的所有的 InternalThreadLocalMap 。


这里的关键点其实就是这个变量:


image.png


static final,能保证 VARIABLE_TO_REMOVE_INDEX 恒等于 0,也就是数组的第一个位置。


用示例程序,给大家演示一下,它第一个位置放的东西:


image.png


在第 21 行打上断点,然后看一下执行完 addToVariablesToRemove 方法后,InternalThreadLocalMap 数组的情况:



image.png


诚不欺你,第 0 个位置上放的是所有的 InternalThreadLocal 的集合。


所以,我们看一下它的 size 方法,就能明白这里为什么要减一了:


那么在第一个位置维护线程里面所有的 InternalThreadLocal 集合的用处是什么?


看看它的 removeAll 方法:


image.png

目录
相关文章
|
1天前
|
Java
面试官:说一说CyclicBarrier的妙用!我:这个没用过...
面试官:说一说CyclicBarrier的妙用!我:这个没用过...
6 2
|
7月前
|
Java 应用服务中间件 Spring
ThreadLocal 的使用及踩坑
ThreadLocal 的使用及踩坑
48 1
面试又被问懵了吗?不如把ThreadLocal拆开了揉碎看看
1.为什么用 ThreadLocal? 所谓并发,就是有限资源需要应对远超资源的访问。解决问题的方法,要么增加资源应对访问;要么增加资源的利用率。 所以,相信这年头做开发的多多少少,都会那么几个“线程二三招”、“用锁五六式”。 那所带来的就是多线程访问下的并发安全问题。 共享变量的访问域跨越了原始的单线程,进入了千家万户的线程眼里。谁都可以用,谁都可以改,那不就打起来了吗? 因此,防止并发问题的最好办法,就是不要多线程访问(这科技水平倒退二十年~)。ThreadLocal 顾名思义,将一个变量限制为“线程封闭”:对象只被一个线程持有、访问、修改。
|
存储 缓存 Java
终于弄明白了ThreadLocal
ThreadLocal是Thread的局部变量,用于编多线程程序,对解决多线程程序的并发问题有一定的启示作用。
113 0
终于弄明白了ThreadLocal
|
算法 安全 Java
threadlocal再温习
早时总结过《ThreadLocal解析》、《FastThreadLocal解析》 最近看一些资料的时候,又重重发现了这类,不希望再温下,许多知识点,之前已经总结了,这篇文章主要有两个问题: 1、弱引用的意义 2、如何防键冲突
199 0
threadlocal再温习
|
监控 C语言 iOS开发
小码哥底层原理之RunLoop
RunLoop是一个运行循环,保证App能够持续运行,处理各种事件,节省CPU资源,没事处理的时候就进入休眠。
128 0
小码哥底层原理之RunLoop
|
存储 安全 算法
面试:为了进阿里,死磕了ConcurrentHashMap源码和面试题(二)
在上篇《面试:为了进阿里,死磕了ConcurrentHashMap源码和面试题(一)》,研究了基础原理,以及ConcurrentHashMap数据put的流程等线程安全的,来回顾一下面试的问题点
142 0
面试:为了进阿里,死磕了ConcurrentHashMap源码和面试题(二)
|
存储 安全 算法
面试:为了进阿里,死磕了ConcurrentHashMap源码和面试题(一)
在平时中集合使用中,当涉及多线程开发时,如果使用HashMap可能会导致死锁问题,使用HashTable效率又不高。而ConcurrentHashMap在保持同步同时并发效率比较高,ConcurrentHashmap是最好的选择,那面试中也会被常常问到,那可能的问题是:
363 0
面试:为了进阿里,死磕了ConcurrentHashMap源码和面试题(一)
|
存储 Java 对象存储
可能有人听过ThreadLocal,但一定没人听过ThreadLocal对象池
JDK中的Thread大家肯定用过,只要是用过异步编程的同学肯定都熟悉。为了保存Thread中特有的变量,JDK引入了ThreadLocal类来专门对Thread的本地变量进行管理。
|
Dubbo 应用服务中间件
这玩意比ThreadLocal叼多了,吓得why哥赶紧分享出来。 (2)
这玩意比ThreadLocal叼多了,吓得why哥赶紧分享出来。 (2)
110 0
这玩意比ThreadLocal叼多了,吓得why哥赶紧分享出来。 (2)