京东二面:为什么Netty要造FastThreadLocal?

FastThreadLocal 从字面意义上来看,它是“Fast”+“ThreadLocal”的结合体,寓意为快速的 ThreadLocal。那么,问题来了,Netty 为什么要再造一个 FastThreadLocal?FastThreadLocal 运行快的原因是啥?除了快之外,它还有其他优势吗?

1.先从ThreadLocal说起

ThreadLocal 线程本地变量,每个线程都拥有一份该变量的独立副本,即使是在多线程环境下,每个线程也只能修改和访问自己的那份副本,从而避免了线程安全问题,实现了线程间的隔离。

ThreadLocal 底层是使用 ThreadLocalMap 实现的,这点从 JDK 的源码中可以看出,核心源码如下:

private void set(Thread t, T value) {ThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {createMap(t, value);}
}

从 ThreadLocal 的 set 方法可以看出,ThreadLocal 是存储在 ThreadLocalMap 中的,咱们继续看 ThreadLocalMap 的源码实现:

图片

从上面源码可以看出,ThreadLocalMap 中存放的是 Entry(哈希桶),而 Entry 中的 key 就是 ThreadLocal,而 value 则是要存储的值,所以我们得出 ThreadLocal 的底层实现如下:

图片

2.ThreadLocal存在的问题

2.1 性能问题

因为 ThreadLocal 底层是使用 ThreadLocalMap 实现的,ThreadLocalMap 类似于哈希表。当一个线程是拥有多个 ThreadLocal 时,ThreadLocalMap 很容易发生 Hash 冲突,此时 ThreadLocal 就不得不使用线性探测法来解决哈希冲突了,而在解决 Hash 冲突时需要不停地向下寻找,效率较低,因此 ThreadLocal 存在的第一个问题就是性能较低。

2.2 内存泄漏问题

ThreadLocal 也存在内存泄漏的问题,具体来说 ThreadLocalMap 使用 ThreadLocal 对象作为键(Key),并且这个键是弱引用(WeakReference)类型。这意味着当没有其他强引用指向 ThreadLocal 对象时,它将会在下次垃圾回收时被回收。然而,Entry 中保存的值(Value)仍然是强引用,这就可能导致以下问题:

  1. 弱引用键的回收:一旦外部对 ThreadLocal 实例的所有强引用消失,ThreadLocal 对象本身就会变为弱可达状态。在下一次垃圾回收时,由于是弱引用,ThreadLocal 对象会被回收,但 Entry 中的 Value(即实际存储的数据)仍然是强引用,因此不会被回收。

  2. Map 引用陷阱:即使 ThreadLocal 键被回收,Entry 仍然存在于 ThreadLocalMap 中,并且由于 Map 对 Entry 的引用,这些 Entry 所持有的 Value 对象也不会被垃圾回收,从而导致这些对象无法被使用也无法被释放,形成了所谓的“内存泄漏”。

  3. 线程长期存活:在一些场景下,特别是使用线程池时,线程的生命周期往往很长,甚至伴随整个应用的生命周期。这意味着 ThreadLocalMap 中的 Entry 可能会长时间不被清理,进一步加剧了内存泄漏问题。

所以,综合来看,在使用 ThreadLocal 时,如果在使用完之后,未及时调用 remove() 方法的话,就会出现内存泄漏的问题。

3.FastThreadLocal特点

为了解决 ThreadLocal 存在的这些问题,所以 Netty 创造出了一个 FastThreadLocal,FastThreadLocal 的特点如下。

3.1 效率高

FastThreadLocal 之所以性能高的原因是因为其存储结构,在 FastThreadLocal 中并没有向 ThreadLocal 那样,使用哈希表来存储元素,而是使用了数组来进行元素存储,它的核心实现源码如下:

public class FastThreadLocal<V> {// FastThreadLocal中的index是记录了该它维护的数据应该存储的位置// InternalThreadLocalMap数组中的下标, 它是在构造函数中确定的private final int index;public InternalThreadLocal() {index = InternalThreadLocalMap.nextVariableIndex();}// 省略其他代码
}

FastThreadLocal 核心类 InternalThreadLocalMap 的实现源码如下:

public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {// 自增索引, ⽤于计算下次存储到Object数组中的位置private static final AtomicInteger nextIndex = new AtomicInteger();private static final int ARRAY_LIST_CAPACITY_MAX_SIZE = Integer.MAX_VALUE - 8;public static int nextVariableIndex() {int index = nextIndex.getAndIncrement();if (index >= ARRAY_LIST_CAPACITY_MAX_SIZE || index < 0) {nextIndex.set(ARRAY_LIST_CAPACITY_MAX_SIZE);throw new IllegalStateException("too many thread-local indexed variables");}return index;}// 省略其他代码
}

从上述源码可以看出,FastThreadLocal 在初始化的时候分配一个数组索引 index,index 的值采用原子类 AtomicInteger 保证顺序递增,通过调用 InternalThreadLocalMap.nextVariableIndex() 方法获得。然后在读写数据的时候通过数组下标 index 直接定位到 FastThreadLocal 的位置,时间复杂度为 O(1)。如果数组下标递增到非常大,那么数组也会比较大,所以 FastThreadLocal 是通过空间换时间的思想提升读写性能。

因此,在 FastThreadLocal 中并不需要使用线性探测法来解决 Hash 冲突,因为它是使用数组进行存储的,每次使用下标进行查询即可,它的查询时间复杂度也是 O(1) 的,所以它的操作效率很高。

3.2 安全性更高

JDK 原生的 ThreadLocal 使用不当可能造成内存泄漏,只能等待线程销毁。然而 FastThreadLocal 却不存在这个问题,在 FastThreadLocal 中不仅提供了 remove() 方法可以主动清除对象,而且它还封装了 FastThreadLocalRunnable,FastThreadLocalRunnable 在最后使用完之后会自动调用 removeAll() 方法将集合中所有对象清理掉,因此 FastThreadLocal 更安全。

FastThreadLocalRunnable 自动清除对象的实现核心源码如下:

final class FastThreadLocalRunnable implements Runnable {private final Runnable runnable;@Overridepublic void run() {try {runnable.run();} finally {FastThreadLocal.removeAll();}}static Runnable wrap(Runnable runnable) {return runnable instanceof FastThreadLocalRunnable ? runnable : new FastThreadLocalRunnable(runnable);}
}

4.小结

FastThreadLocal 相比于 ThreadLocal 存在以下两个主要优点:

  1. 性能更高:FastThreadLocal 使用了数组的方式来存储元素,所以它的查询时间复杂度 O(1) 相比于 ThreadLocal 的哈希表操作效率更高。

  2. 安全性更高:FastThreadLocal 中的 FastThreadLocalRunnable 在最后执行完之后会自动调用 removeAll() 将集合中所有对象都清理掉,可以避免内存泄漏的问题,所以它的安全性更高。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/18691.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linnux上安装php zip(ZipArchive)、libzip扩展

安装顺序&#xff1a; 安装zip&#xff08;ZipArchive&#xff09;&#xff0c;需要先安装libzip扩展 安装libzip&#xff0c;需要先安装cmake 按照cmake、libzip、zip的先后顺序安装 下面的命令都是Linux命令 1、安装cmake 确认是否已安装 cmake --version cmake官网 未安装…

项目十 网络服务器搭建及应用

文章目录 Samba1.配置yum源2.安装samba服务和客户端3.添加用户4.修改访问控制权限&#xff0c;使samba指定的目录可以正常访问5.修改samba配置参数6.启动samba服务7.查看samba运行情况8.通过外部服务器访问 I know, i know 地球另一端有你陪我 Samba 1.配置yum源 mkdi…

【Paddle】稀疏计算的使用指南 稀疏ResNet的学习心得 (2) + Paddle3D应用实例稀疏 ResNet代码解读 (1.6w字超详细)

【Paddle】稀疏计算的使用指南 & 稀疏ResNet的学习心得 Paddle3D应用实例稀疏 ResNet代码解读 写在最前面一、稀疏格式简介1. COO&#xff08;Coordinate Format&#xff09;2. CSR&#xff08;Compressed Sparse Row Format&#xff09; 二、Paddle稀疏张量支持1. 创建 C…

SQL刷题笔记day6——转战LeetCode

1 第二高的薪水 ​ 我的代码&#xff1a; SELECT Salary SecondHighestSalary FROM Employee ORDER BY Salary DESC LIMIT 1, 1 我的代码不满足示例2的情况&#xff1a;如果没有第 2 高的薪资&#xff0c;即表里可能只有一条记录&#xff0c;这个解答会被评测为 Wrong Answ…

整理了六个正规靠谱的兼职赚钱软件,适合普通人做的兼职副业~

​随着互联网时代的到来&#xff0c;越来越多的人选择通过互联网赚钱。在这篇文章中&#xff0c;我们将探讨一些可以在网上长期赚钱的方法。 在网络上面其实有很多的赚钱方法&#xff0c;尽管方法很多&#xff0c;但是对于一些网络新手&#xff0c;刚进入互联网圈子不久的伙伴…

智慧校园建设的重要性有哪些

在21世纪的数字化浪潮中&#xff0c;教育领域正经历一场深刻的变革。智慧校园&#xff0c;这一概念如同一股清风&#xff0c;席卷全球的高等教育机构&#xff0c;以其创新的科技应用和教育理念&#xff0c;重塑着学习的未来。面对信息时代的挑战&#xff0c;传统校园模式是否还…

html+CSS部分基础运用9

项目1 参会注册表 1.设计参会注册表页面&#xff0c;效果如图9-1所示。 图9-1 参会注册表页面 项目2 设计《大学生暑期社会实践调查问卷》 1.设计“大学生暑期社会实践调查问卷”页面&#xff0c;如图9-2所示。 图9-2 大学生暑期社会调查表页面 2&#xff0e;调查表前导语的…

Win11有些exe双击后无反应的解决办法

现象 双击某些exe文件之后&#xff0c;小圆圈转了两下之后就消失&#xff0c;然后没任何反应。用回车反复启动也是一样的现象。 由于截图没法截图到鼠标&#xff0c;所以没法放出截图。 我电脑出现上述现象的软件有&#xff1a; 1.纸飞机调试助手 2.SOC Programming Tool 对…

torch-cuda版本安装

在gpu机器上 torch.cuda.device_count() 0 是什么原因呢 没有安装torch的GPU版本&#xff0c;无法将模型或数据提交到gpu上训练。 cuda_available torch.cuda.is_available() print("Is CUDA available:", cuda_available) # 输出为Falsedevice_count torch.cud…

3.4 移动机器人工作空间(摘自自主移动机器人导论2)

对于一个机器人来说&#xff0c;机动性等效于它的控制自由度。但是&#xff0c;机器人是处于某种环境的&#xff0c;因而下一个问题是把我们的分析放到环境之中。 我们关心机器人用它可控制的自由度在环境中定位它本身的方法。例如&#xff0c;考虑 Ackerman 车辆或汽车&#…

《深入C++11 代码优化与工程应用》勘误表(二)

第4章 1. 书中做了个包装函数&#xff0c;p是第三方库返回的裸指针。此包装函数为了用智能指针包裹以起到自动释放堆内存的目的。但是代码中lambda捕获的this指针只能在类中使用&#xff0c;否则编译报错&#xff1a; void* p GetHandle()->Create();shared_ptr<void&…

Pyhton 二叉树层级遍历

class TreeNode:def __init__(self, val0, leftNone, rightNone):self.val valself.left leftself.right rightclass Solution:def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:res []# 空节点&#xff0c;直接返回if not root:return resque [roo…

Docker容器快速入门(1)

目录 1.Docker 简介 2.跟普通虚拟机的对比 2.打包、分发、部署 Docker主要理念&#xff1a;一次封装&#xff08;打包&#xff09;随处运行&#xff08;部署&#xff09; 4.Docker 部署的优势 5.Docker 通常用来做什么 6.重要概念&#xff1a;镜像、容器、仓库 Docker 官方…

c++移动构造和赋值的样例

#include <iostream>class MyResource { public:// 默认构造函数MyResource(size_t size 0) : m_size(size), m_data(size ? new int[size] : nullptr) {std::cout << "Default constructor called\n";}// 析构函数~MyResource() {delete[] m_data;std…

使用ssh连接ubuntu

一、下载连接工具 常见的连接工具右fianlshell、xshell等等。在本文章中使用的finalshell&#xff0c;工具可以去官网上下载&#xff0c;官网下载。 二、Ubuntu中配置shh 1、使用下面指令更新软件包&#xff08;常用于下载安装或更新软件时使用&#xff0c;更新到最新的安装…

图搜索算法A*、Dijkstra在路径规划中的应用

当我们讨论基础路径规划算法时&#xff0c;尤其是在图搜索算法的范畴内&#xff0c;通常会重点关注如A*和Dijkstra算法这样的经典方法。这些算法在多种场景中&#xff0c;如机器人导航、网络路由以及游戏设计中发挥着至关重要的作用。下面为您提供一个学习大纲&#xff0c;帮助…

红苹果的圆度、直径大小标定

function fruitImageProcessingGUI% 创建主窗口和控件mainFigure figure(Units, normalized, Position, [0.3, 0.3, 0.4, 0.4]);instructionText uicontrol(Style, text, String, 请点击按钮执行相应的图像处理步骤, ...Units, normalized, Position, [0.1, 0.7, 0.8, 0.2], …

母亲的爱与妻子的爱,同为“爱“。不同感受!

母亲的爱与妻子的爱&#xff0c;虽然都是一个女人给予男人的爱&#xff0c;却有着本质的不同&#xff01; 天下父母对儿女的爱大多相同。在母亲眼中&#xff0c;儿女无论是多大年龄&#xff0c;无论你是否长大成人&#xff0c;也无论你做多大的官&#xff0c;有多么大的成就&am…

HBuilderX新增uni-app项目并发布到微信小程序

目录 1、下载软件并安装 2、创建项目并配置小程序id 3、微信开发者工具运行项目并配置 4、开发一个登录页面并发布 5、上传代码并小程序打开 6、手机扫码查看小程序 7、体验完后还要发版要去小程序申请备案认证 1、下载软件并安装 下载HBuilderX 下载微信开发者工具 …

Java中的对象流 ObjectInputStream 和ObjectOutputStream

ObjectInputStream和ObjectOutputStream是Java中用于对象序列化和反序列化的重要工具&#xff0c;它们属于Java I/O库中的对象流。 基本功能&#xff1a; ObjectOutputStream&#xff1a;主要用于将Java对象&#xff08;必须是实现了Serializable接口的类的对象&#xff09;写…