源码解析:ThreadLocal(2)

2023-05-09 21:48:46 浏览数 (1)

3.3> nextIndex和prevIndex

  • 我们先来看第一个红框中的方法nextIndex(i, len),其实通过该方法,我们还可以引出prevIndex(i, len)方法。源码和注释如下所示:

【解释】

通过源码,我们可以得出以下结论:

nextIndex就是从指定的下标i开始,向后获取下一个位置的下标值。

preIndex就是从指定的下标i开始,前向获取上一个位置的下标值。

那么,如果越界了怎么办呢?它们会采用循环查找法。即:获取队尾的下一个下标就会返回队首的下标;获取队首的上一个下标就会返回队尾的下标。如下所示:


3.4> 开放地址法

  • 当我们看到这里,就发现这个ThreadLocalMap有些“奇怪”,它并没有按照我们之前在学习HashMap的方式去解决哈希冲突,即:数组 链表。而它其实使用的是一种叫做“开放地址法”作为解决哈希冲突的一种方式。
  • 什么是开放地址法呢?

开放地址法的基本思想就是:一旦发生了冲突,那么就去寻找下一个空的地址;那么只要表足够大,空的地址总能找到,并将记录插入进去。

  • ThreadLocalMap和HashMap的区别是什么呢?
    • HashMap
      • 数据结构是数组 链表
      • 通过链地址法解决hash冲突的问题
      • 里面的Entry内部类的引用都是强引用
    • ThreadLocalMap
      • 数据结构仅仅是数组
      • 通过开放地址法来解决hash冲突的问题
      • Entry内部类中的key是弱引用,value是强引用
  • 链地址法和开放地址法的优缺点是什么呢?
    • 开放地址法
      • 容易产生堆积问题,不适于大规模的数据存储。
      • 散列函数的设计对冲突会有很大的影响,插入时可能会出现多次冲突的现象。
      • 删除的元素是多个冲突元素中的一个,需要对后面的元素作处理,实现较复杂。
    • 链地址法:
      • 处理冲突简单,且无堆积现象,平均查找长度短。
      • 链表中的结点是动态申请的,适合构造表不能确定长度的情况。
      • 删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。
      • 指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间。
  • ThreadLocalMap采用开放地址法原因是什么?
    • ThreadLocal往往存放的数据量不会特别大(而且key 是弱引用又会被垃圾回收,及时让数据量更小)。
    • 采用开放地址法简单的结构会更节省空间,同时数组的查询效率也是非常高,加上第一点的保障,冲突概率也比较低。
  • 那么,当我们了解了开发地址法的原理之后,我们就有了看懂下面代码的重要武器了。好,那我们继续看下面的源码

后面的内容,参见:ThreadLocal源码精讲(3)

0 人点赞