ThreadLocal

为什么要使用 ThreadLocal 进行登录时处理用户信息?而非普通变量?
假如有两个用户 A 和 B,他们分别进行登录,并且他们的每次请求都会带有自己的 token,在请求到达 controller 之前(preHandle() 中),每次都会被会被拦截器进行拦截,提取出当前 token 中的用户信息(比如 userId),认证通过以后在 service 中就可以通过 Contenxt 类获取提取出来的用户信息。

前提
Context 类中存储用户的 ID,有一个静态的变量或者对象叫做 USER_ID。
(1)使用普通变量,例如 String
Context 类使用 String 变量 (static String USER_ID) 进行存储,这时候:

用户 A 登录,带着自己的 token,到达后端拦截器,token 验证通过后,用户信息被提取到 Context 中的 String 变量中。
用户 B 登录,带着自己的 token,到达后端拦截器,token 验证通过后,用户信息被提取到 Context 中的 String 变量中。(这里 String 变量之前存储的是 A 的信息,但是由于 B 登录以后,又将 String 的值设置为了 B 的 token 中提取出来的用户信息。)
用户 A 调用新增的 api,这时候调用新增 api 的这个请求,也附带了 A 的 token 信息,所以重复第一步。
用户 B 调用新增的 api,这时候调用新增 api 的这个请求,也附带了 B 的 token 信息,所以重复第二步。
虽然存储用户信息都是在一个 String 中,但是好像并没有发现什么问题。(往下看)
(2)使用 ThreadLocal 类进行存储
Context 类使用 ThreadLocal 类型的对象 (ThreadLocal USER_ID = new …) 进行存储,这时候:

用户 A 登录,带着自己的 token,到达后端拦截器,token 验证通过后,接下来应该提取用户信息到 Context 中了,这时候,用户 A 当前登录是在一个线程 ThreadA 中,那么看到 Context 中定义的 USER_ID 是 ThreadLocal 类型的,(简单讲)这时候他会以当前线程为 key = ThreadA,以 A.token 为 value 创建一个新的只属于当前 ThreadA 的对象 USER_ID。
用户 B 登录,带着自己的 token,到达后端拦截器,token 验证通过后,接下来应该提取用户信息到 Context 中了,这时候,用户 B 当前登录是在一个线程 ThreadB 中,那么看到 Context 中定义的 USER_ID 是 ThreadLocal 类型的,(简单讲)这时候他会以当前线程为 key= ThreadB,以 B.token 为 value 创建一个新的只属于当前 ThreadB 的对象 USER_ID。
总结
第一种方式看似运行时和第二种没有区别,但是在高并发的时候,由于 USER_ID 的值的设置和 USER_ID 的值的获取是两次操作,那么很显然设置和获取不是一个原子性的操作,这时候肯定会发生并发问题,即:A 刚设置了值,还没有等到 A 取值,B 就将这个 String 类型的 USER_ID 设置成了自己的信息。这时候 A 再进行取值,取到的就是 B 的值。
第二种方式的话,很显然就解决了这个问题,因为他们都是操作的自己线程内的 USRE_ID,各个线程之间互不影响,所以这个时候,完全不会混乱。
注意
首先说一个名词 OOM,即 Out Of Memory,内存泄露、内存溢出。
ThreadLocal 中的 key 是弱引用,value 是强引用。
弱引用,自动垃圾回收。
强引用,线程销毁时,才会被回收。
一个线程可能有时候很久都不会被销毁,但是这时候只有弱引用的 key 会被回收,value 由于是强引用,由于线程还存在,他就会存在,但是 value 已经没有用了,这个时候就造成了浪费。
为了避免浪费内存,继而发生内存溢出,我们需要使用 remove() 方法,进行手动清除 ThreadLocal 对象。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值