threadlocal底层原理,ThreadLocal原理

首页 > 技术 > 作者:YD1662023-04-15 23:51:07

引言

这是JWT认证条件下的getCurrentLoginUser代码实现,请分析性能:

在生产环境中,currentLoginUser永远为null,if不执行。

执行else内的解析JWT的代码,解析userId,再查询用户。

很明显,一次请求内,当getCurrentLoginUser被多次调用时,会重复解析JWT,就会产生性能问题。

解决

分析

解决重复解析JWT的唯一思路就是缓存,第一次解析完userId并查询出user后将这个user对象缓存。

因为并发请求时,每个请求分配一个线程管理Socket。

所以当前待解决的问题就变成了:如何设计一种缓存,使之各线程不影响,线程安全。

threadlocal底层原理,ThreadLocal原理(1)

简单的设计如上,一个Map,Thread作为key,用户缓存作为value。

ThreadLocal

ThreadLocal是啥?没听说过是不是?其实这是Java里最基础的东西。我的Java到底学了个啥呀?

日常吐槽,我发现其他学校竟然讲spring-boot、spring-cloud、ConcurrentHashMap源码,人家上完专业课直接精通kafka。

一般人知道HashMap的阀值为什么是8吗?

对不起,这些人家老师都讲过。而《河北工业大学》的“大博士”,你讲个检查异常都讲错了,被学生指出之后还不改。你去学学Java再来讲课好吗?

最后的结果就是,我们辛辛苦苦准备了好几个月的东西,人家课上就精通完了。怪不得干不过人家,我们引以为傲的spring-cloud、RPC原来都属于课上的基础知识。

继续。

我们想用一个类似Map<Thread, User>这样的数据结构来设计缓存,其实JDK中早就为我们封装好了,即ThreadLocal<T>。

栗子

大家来看下面的示例代码:

运行结果如下:

threadlocal底层原理,ThreadLocal原理(2)

main线程创建ThreadLocal对象,thread1、thread2操作的是同一个对象local,thread1、thread2分别set数据,两线程再次从local中获取数据的时候,能够保证两者数据不冲突。

底层原理

ThreadLocal中的set方法实现如下:

获取当前线程,同时通过线程对象获取ThreadLocalMap。

set内调用了getMap方法,看看getMap的内部实现:

返回线程对象内的threadLocals属性。

Thread类中的成员属性threadLocals,默认为null。

threadlocal底层原理,ThreadLocal原理(3)

接着看:获取到了Map之后,用当前的ThreadLocal对象作为key存储value进Map。

这样设计十分地精妙,每一个线程都有独立的Map存储,肯定能做到数据隔离且安全。且可方便地创建多个安全的ThreadLocal进行存储。

改写

鉴于ThreadLocal的特性,我们可以设计一个SecurityContext以封装ThreadLocal<User>。

写一个拦截器,思路如下:

在pre里解析JWT,并存到SecurityContext里。

在post里clear,防止线程池线程复用导致数据错误。

然后原来的getCurrentLoginUser方法直接从SecurityContext中get即可。

熟悉吗?

看到SecurityContext这个名称是不是很熟悉?

从spring-security中获取用户信息的方法如下,它怎么实现的呢?

点开spring-security源码,有三种策略:GlobalSecurityContextHolderStrategy(即全局线程共享策略)、ThreadLocalSecurityContextHolderStrategy(本地策略)、InheritableThreadLocalSecurityContextHolderStrategy(可继承的本地策略)。

threadlocal底层原理,ThreadLocal原理(4)

点开源码后发现,其实spring-security就是这么简单,内部也是用ThreadLocal实现的,只是此处存储的信息比较多,使用ThreadLocal<SecurityContext>。

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.