ThreadLocal的一些缺陷

Scroll Down

概述

本文主要讲述了ThreadLocal存储的一些缺陷,以及如何通过其他办法弥补这些缺陷。

ThreadLocal的缺陷

首先ThreadLocal的缺陷其实是因为开放定址法解决哈希冲突这种方式导致的。这种解决冲突的方式的优点是实现简单,对于key不多的情况下,性能也不存在问题。但是当数据量多起来(当然我们现实的使用场景可能不会那么多,但是还是会存在哈希计算和寻址的问题),会有一定的性能问题。

开放定址法存储数据的两个缺点

  • 1、开放定址发存储数据是根据key的哈希值计算出一个数组的下标A,每个计算出相同的下标A的元素存放的时候都是从头到尾优先找到不为空的元素然后放进去。有个特殊情况就是比如一个元素计算出来的下标是11,然后这时又有一个元素计算的下标也是11,那么这时候只能放到12上面,这时有个元素根据hash值计算出来的下标是12那么这时候需要把元素放到13上面,那么这时就出现了hash计算出来的key11和key12元素交替的情况,这时也是可以继续运行的,通过元素的下一个节点一直去判断。但是这种情况比较耗费性能,因为在查找期间有不属于这个key的元素也在里面,查找效率不高。这是开放定址法的一个缺点。
  • 2、另外一个缺点是,会造成空间的碎片化。比如key1和key1001的情况,只要后续没有key计算1-1001的情况,那么这段空间永远无法被使用到。

那么这种缺陷有办法避免吗?

Netty中对ThreadLocal进行了改造。它改造的思想是空间换时间的思想(但是Netty中的对象排序是紧凑型的,不见得会浪费多少空间),在寻找元素的时候是可以做到O(1)的,那么Netty中是怎么实现的呢?其实很简单。
Netty中使用了FastThreadLocal和FastThreadLocalThread来代表新的ThreadLocal的使用方式,其使用与JDK一样。

FastThreadLocal

我们这里重点讲一下Netty中的InternalThreadLocalMap存储原理。
InternalThreadLocalMap中也是定义了一个数组,特殊的地方就是这个数组的第0位存储了当前线程的所有ThreadLocal的引用集合(Set<FastThreadLocal>),然后后面依次按照FastThreadLocal的创建的顺序将值放入对应的下标中。每个FastThreadLocal的index值是固定的,从创建对象的时候就计算好的。这样,我们在每次获取数据的时候可以直接通过下标的方式获取值。
什么意思?就是每往 InternalThreadLocalMap 中塞入一个新的 FastThreadLocal 对象,就给这个对象发个唯一的下标,然后让这个对象记住这个下标,到时候去 InternalThreadLocalMap 找 value 的时候,直接通过下标去取对应的 value 。这样不就不会冲突了?这就是 FastThreadLocal 给出的方案,具体下面分析。
https://www.zhihu.com/question/602681495/answer/3081281023?utm_id=0