黑马点评双拦截器和Threadlocal实现原理

文章目录

    • 双拦截器
    • ThreadLocal实现原理

双拦截器

实现登录状态刷新的原因:

防止用户会话过期:通过动态刷新Token有效期,确保活跃用户不会因固定过期时间而被强制登出

提升用户体验:用户无需频繁重新登录,只要在活动期间就能保持登录状态

在登录状态校验时,只设置了一个拦截器:

只设置一个拦截器时:拦截器只拦截刷新需要登录的路径,如果用户在登录后长时间不访问任何需要拦截的路径,那么他们的登录令牌可能不会得到及时刷新,导致令牌过期。一旦用户尝试访问需要拦截的路径,他们可能会发现自己需要重新登录,因为令牌已经失效。所以单独创建一个拦截器拦截一切请求,刷新Redis中的Key

双拦截器执行流程:

那么我们可以添加一个拦截器,第一个拦截器拦截所有路径,首先从请求头中获取token,如果token存在,则通过token查询redis判断判断该token是否存在redis中(有可能是别的网站的token,所以要判断是否在redis中)和有没有过期(过期了会采用过期淘汰策略…有可能直接查不到/查到过期了就直接删除),如果token存在reids中且没有过期,将用户信息(UserDTO对象,包括用户id、昵称等)保存到theadlocal然后刷新token有效期并放行,如果token不存在或过期了则不执行任何操作并放行。

第一个拦截器只进行刷新token操作不拦截,第二个拦截器拦截需要登录的路径,判断ThreadLocal中的是否存在用户信息,如果用户存在就说明登录了就放行,否则执行拦截操作。(只创建了一个threadlocal线程对象,threadlocalmap中只存了一个对象,直接判断是否为空就可以)

token来源:注册登录后,后端会生成一个token作为用户的唯一id,将这个token作为key用户信息作为value存入redis中(还设置有效期),同时将这个token返回给前端,前端的每次请求都会携带这个token进行登录状态校验操作。

Threadlocal中存入的用户信息的作用

  1. 在第二个拦截器中可以用来判断是否存在用户信息,进而完成用户登录拦截操作
  2. 在后面一人一单判断过程中,需要从Threadlocal中取出用户id来构造用户级细粒度锁。

未登录拦截的实现:如果需要登录拦截,则返回HTTP状态码为401(未授权),前端根据状态码跳转到登录页。

拦截器使用:1.定义拦截器 2.注册配置拦截器

双拦截器通过设置优先级来实现执行的先后顺序(第一个拦截器优先级高order=0,第二个拦截器order=1)

ThreadLocal实现原理

客户端每一次发起的请求都是单独的一个线程,所以可以用ThreadLocal

ThreadLocal 是 Java 中的一个工具类,通过threadlocal类可以创建线程对象,threadlocal会在每个线程内开辟一个内存空间去保存每个线程的数据,可以实现线程间的数据隔离,避免线程安全问题。

ThreadLocal是用于解决线程安全的一种机制,它允许创建线程局部变量,每个线程自己独立的变量副本,从而避免了线程之间的资源共享和同步问题。

这里说的副本指的是每个线程拥有该变量的独立实例,线程之间不会共享相同的变量,从而实现了线程隔离和数据安全。

ThreadLocal 的作用

  1. 线程隔离: 每个线程拥有自己的变量副本,互不干扰。
  2. 避免共享: 无需使用锁或同步机制,提升并发性能。
  3. 简化设计: 方便在多线程环境中传递上下文信息(如用户会话、事务 ID)。

ThreadLocal 的实现原理

主要是通过Thread类中的ThreadLocalMap字段来实现的。

  • ThreadLocalMap: 每个线程内部都有自己的 ThreadLocalMap,用于存储 ThreadLocal 变量,一个线程可以创建多个ThreadLocal线程对象,如ThreadLocal1、ThreadLocal2等,存在ThreadLocalMap中的不同位置。
  • 键值对存储ThreadLocal对象本身作为键,变量副本作为值。

ThreadLocal 的常用方法

  1. public void set(T value) 设置当前线程的线程局部变量的值
  2. public T get() 返回当前线程所对应的线程局部变量的值
  3. public void remove() 移除当前线程的线程局部变量

使用场景

  1. 线程上下文传递: 如用户会话、事务 ID。
  2. 数据库连接管理: 每个线程使用独立的数据库连接。
  3. 日期格式化SimpleDateFormat 非线程安全,可使用 ThreadLocal 为每个线程创建独立实例。

ThreadLocalMap 只由数组组成,通过开放地址法中的线性探测(线性向后查找)的方式解决hash冲突。具体的:如果 i 位置被占用,尝试 i+1。如果 i+1 也被占用,继续探测 i+2,直到找到一个空位。如果到达数组末尾,则回到数组头部,继续寻找空位。

为什么用线性探测法而不用hashmap的拉链法?因为ThreadLocalMap 不会有大量的 Key,所以采用线性探测更节省空间。

GC 之后 key 是否为 null? 是null,因为key是弱引用,gc回收后,key为null,但是value是强引用,垃圾回收后还会存在。

用完之后要及时执行remove方法

ThreadLocalMap 扩容机制

采用的是“先清理再扩容”的策略,元素个数达到阈值(0.75*总容量)时,会先清理掉被垃圾回收掉key的entry对象,然后再检查size是否到阈值,扩容时,数组长度翻倍,并重新计算索引,如果发生哈希冲突,采用线性探测法来解决。

使用 InheritableThreadLocal 时,会在创建子线程时,令子线程继承父线程中的 ThreadLocal 值,但是无法支持线程池场景下的 ThreadLocal 值传递。

还有TransmittableThreadLocal:TransimittableTreadLocal 是 TreadLocal 的增强。它与InheritableThreadLocal 相比,更适合在线程池中父线程与子线程传递的场景。ITL 只是在子线程被创建时继承一次父线程的值,之后如果子线程自己修改了值,就会一直复用这个值,不会拉取父线程的值,并且也感知不到父线程值得变化。而 TTL,是任务级别的动态捕获,每次任务提交时,会动态捕获父线程的最新值。通过捕获上下文、传递上下文、恢复上下文的方式完成。 链接

每个线程都维护一个ThreadLocalMap,一个线程可以创建多个线程对象
public class MultipleThreadLocalsDemo {// 定义多个ThreadLocal变量private static final ThreadLocal<String> userContext = new ThreadLocal<>();private static final ThreadLocal<Integer> requestId = new ThreadLocal<>();private static final ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));public static void main(String[] args) {// 主线程设置多个ThreadLocal值userContext.set("用户A");requestId.set(1001);// 获取值(互不干扰)System.out.println(userContext.get());  // 输出"用户A"System.out.println(requestId.get());    // 输出1001System.out.println(dateFormat.get().format(new Date())); // 输出当前日期// 必须显式清理(防止内存泄漏)userContext.remove();requestId.remove();dateFormat.remove();}
}

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

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

相关文章

Windows 中动态库.dll 的 .lib 文件有什么作用?

在 Windows 平台开发中, 动态链接库(Dynamic Link Library, DLL)。与之相关的还有一个常让人困惑的文件——.lib 文件。那么,这个 .lib 文件到底有什么作用呢? 一、什么是 .lib 文件? .lib 文件是 静态导入库(Import Library) 文件,它通常与动态链接库(DLL)一起生成…

细说STM32单片机FreeRTOS消息缓冲区及其应用实例

目录 一、消息缓冲区功能概述 二、消息缓冲区操作相关函数 1、相关函数概述 2、部分函数详解 &#xff08;1&#xff09;创建消息缓冲区 &#xff08;2&#xff09;写入消息 &#xff08;3&#xff09;读取消息 &#xff08;4&#xff09;消息缓冲区状态查询 三、消息…

【缓存】JAVA本地缓存推荐Caffeine和Guava

&#x1f31f; 引言 在软件开发过程中&#xff0c;缓存是提升系统性能的常用手段。对于基础场景&#xff0c;直接使用 Java集合框架&#xff08;如Map/Set/List&#xff09;即可满足需求。然而&#xff0c;当面对更复杂的缓存场景时&#xff1a; 需要支持多种过期策略&#x…

IDA插件 MIPSROP的安装和使用方法

前言 笔者的IDA版本为9.0&#xff0c;刚开始根据一些博客描述以为将mipsrop.py拷贝到IDA的plugins目录即可&#xff0c;可操作后发现事情好像没这么简单&#xff0c;复制进去后就发现没有博客中所说的 MIPS ROP Finder &#xff0c;笔者在网上搜索了很多博客后在 https://bbs.…

(1)转置后,行列式的值不变 (2)将行列式的任意两行互换位置后,行列式改变符号

以下是对原始内容在不改变内容本身的前提下进行的格式优化&#xff0c;以提升可读性和逻辑清晰度&#xff1a; ✅ 行列式的几何意义 行列式&#xff08;determinant&#xff09;是线性代数中一个非常重要的概念&#xff0c;它的几何含义可以从以下几个方面理解&#xff1a; &a…

最大似然估计(Maximum Likelihood Estimation, MLE)详解

一、定义 最大似然估计 是一种参数估计方法&#xff0c;其核心思想是&#xff1a; 选择能使观测数据出现概率最大的参数值作为估计值。 具体来说&#xff0c;假设数据 D x 1 , x 2 , … , x n D{x_1,x_2,…,x_n} Dx1​,x2​,…,xn​独立且服从某个概率分布 P ( x ∣ θ ) P(…

用go从零构建写一个RPC(3)--异步调用+多路复用实现

在前两个版本中&#xff0c;我们实现了基础的客户端-服务端通信、连接池、序列化等关键模块。为了进一步提升吞吐量和并发性能&#xff0c;本版本新增了 异步发送机制 和 多路复用支持&#xff0c;旨在减少资源消耗、提升连接利用率。 代码地址&#xff1a;https://github.com/…

FFmpeg 安装包全攻略:gpl、lgpl、shared、master 区别详解

这些 FFmpeg 安装包有很多版本和变种&#xff0c;主要区别在于以下几个方面&#xff1a; ✅ 一、从名称中看出的关键参数&#xff1a; 1. 版本号 master&#xff1a;开发版&#xff0c;最新功能&#xff0c;但可能不稳定。n6.1 / n7.1&#xff1a;正式版本&#xff0c;更稳定…

深度学习实战:从图像分类到文本生成的完整案例解析

1 图像分类案例 1.1 CIFAR10数据集介绍 cifar数据是torchvision第三方包提供的数据集 训练集5w 测试集1w y标签 10个类别 10分类问题 一张图形状 (32, 32, 3) import torch import torch.nn as nn from torchvision.datasets import CIFAR10 from torchvision.transforms i…

Android 添加系统服务的完整流程

[应用程序] (应用进程)│↓ 调用简单API [SoundManager] │ ├─ 代理模式门面模式&#xff08;应用进程&#xff09;│ ├─ 缓存数据 ←─ 装饰器模式&#xff08;应用进程&#xff09;│ └─ 转换异常 ←─ 适配器模式&#xff08;应用进程&#xff09;│↓ 通过Bind…

wan2.1代码笔记

GPU内存不够&#xff0c;可以先运行umt5&#xff0c;然后再运行wanpipeline&#xff0c;参考FLUX.1代码笔记&#xff0c;或者使用ComfyUI。 下面使用随机数代替umt5 embedding。 import torch from diffusers.utils import export_to_video from diffusers import Autoencoder…

环境搭建与工具配置

3.1 本地环境搭建 3.1.1 WAMP环境搭建漏洞靶场&#xff08;一、二&#xff09; WAMP&#xff08;Windows Apache MySQL PHP&#xff09;是搭建本地Web漏洞靶场的基础环境。 安装步骤&#xff1a; Apache&#xff1a;下载并安装最新版Apache HTTP Server&#xff0c;配置监…

STM32F446主时钟失效时DAC输出异常现象解析与解决方案

—### 现象概述 在STM32F446微控制器应用中&#xff0c;若主时钟&#xff08;HSE&#xff09;的晶体信号对地短路&#xff0c;但DAC&#xff08;数模转换器&#xff09;仍能输出变化信号&#xff0c;这一现象看似矛盾&#xff0c;实则与系统时钟切换机制密切相关。本文将从硬件…

React 如何封装一个可复用的 Ant Design 组件

文章目录 前言一、为什么需要封装组件&#xff1f;二、 仿antd组件的Button按钮三、封装一个可复用的表格组件 (实战)1. 明确需求2. 设计组件 API3. 实现组件代码4. 使用组件 三、封装组件的最佳实践四、进阶优化 总结 前言 作为一名前端开发工程师&#xff0c;在日常项目中&a…

STC89C52RC/LE52RC

STC89C52RC 芯片手册原理图扩展版原理图 功能示例LED灯LED灯的常亮效果LED灯的闪烁LED灯的跑马灯效果&#xff1a;从左到右&#xff0c;从右到左 数码管静态数码管数码管计数mian.cApp.cApp.hCom.cCom.hDir.cDir.hInt.cInt.hMid.cMid.h 模板mian.cApp.cApp.hCom.cCom.hDir.cDir…

踩坑记录:RecyclerView 局部刷新notifyItemChanged多次调用只触发一次 onBindViewHolder 的原因

1. 问题背景 在做项目的时候&#xff0c;RecyclerView需要使用局部刷新&#xff0c;使用 notifyItemChanged(position, payload) 实现局部刷新&#xff0c;但发现调用多次只执行了一次&#xff0c;第二个刷新不生效。 2. 错误示例&#xff08;只处理 payloads.get(0)&#xff…

OpenLayers 加载鹰眼控件

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图控件是一些用来与地图进行简单交互的工具&#xff0c;地图库预先封装好&#xff0c;可以供开发者直接使用。OpenLayers具有大部分常用的控件&#x…

WPF···

设置启动页 默认最后一个窗口关闭,程序退出,可以设置 修改窗体的icon图标 修改项目exe图标 双击项目名会看到代码 其他 在A窗体点击按钮打开B窗体,在B窗体设置WindowStartupLocation=“CenterOwner” 在A窗体的代码设置 B.Owner = this; B.Show(); B窗体生成在A窗体中间…

github公开项目爬取

import requestsdef search_github_repositories(keyword, tokenNone, languageNone, max_results1000):"""通过 GitHub API 搜索仓库&#xff0c;支持分页获取所有结果&#xff08;最多 1000 条&#xff09;:param keyword: 搜索关键词:param token: GitHub To…

防震基座在半导体晶圆制造设备抛光机详细应用案例-江苏泊苏系统集成有限公司

在半导体制造领域&#xff0c;晶圆抛光作为关键工序&#xff0c;对设备稳定性要求近乎苛刻。哪怕极其细微的振动&#xff0c;都可能对晶圆表面质量产生严重影响&#xff0c;进而左右芯片制造的成败。以下为您呈现一个防震基座在半导体晶圆制造设备抛光机上的经典应用案例。 企…