ThreadLocal概念以及使用场景
ThreadLocal概念以及使用场景
根据自身的知识深度,这里只限于自己使用和学习的知识点整理,原理的解释还需要再沉淀。
该文章从项目开发中举例,希望能帮助到各位,不了解ThreadLocal的朋友,可能会问,这是个是什么,这有什么用,这能用在哪些地方,接下来我一一解释,如果有地方解释不好,希望多多包含。
概念:
这是个是什么:
ThreadLocal是一个类,是一个本地线程,提供了一种线程安全的方式,用来避免共享数据(线程变量隔离)。
翻看源码可以看到注释(已翻译):
此类提供线程局部变量。 这些变量不同于它们的普通对应变量,因为每个访问一个(通过其get或set方法)的线程都有自己的、独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程相关联的类中的私有静态字段(例如,用户 ID 或事务 ID)
这有什么用:
按照《Java核心卷一》来说,有时候可能要避免共享变量,使用ThreadLocal辅助类为各个线程提供各自的实例;
就是说,每个线程都有一个伴生的空间(ThreadLocal),存储私有的数据。
只要线程存在,就能拿到对应的ThreadLocal中存储的值。
创建以及提供的方法
创建一个线程局部变量,其初始值通过调用给定的提供者(Supplier)生成;
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}
使用的方法也比较少:
这里就列出用的比较多的方法:
返回此线程局部变量的当前线程副本中的值。 如果该变量对于当前线程没有值,则首先将其初始化为调用initialValue方法返回的值:
T get()
将此线程局部变量的当前线程副本设置为指定值;
value -- 要存储在此线程本地的当前线程副本中的值
void set(T value)
删除此线程局部变量的当前线程值(删除这里有些讲究,暂且不表,留在后面)
void remove()
项目实例:
ThreadLocal使用的场景主要是:(引用)
每个线程需要自己独立的实例且该实例需要在多个方法中使用
以下是个人使用的场景:
自己为什么会使用它,我是想在项目中直接获取当前用户的信息,这个功能就使用了ThreadLocal;
首先创建一个ThreadLocal类:
import com.xbhog.springbootvueblog.pojo.SysUser;
public class UserThreadLocal {
private UserThreadLocal(){}
private static final ThreadLocal<SysUser> LOCAL = new ThreadLocal<>();
public static void put(SysUser sysUser){
LOCAL.set(sysUser);
}
public static SysUser get(){
return LOCAL.get();
}
public static void remove(){
LOCAL.remove();
}
}
其中包含了数据的添加删除和获取,因为我们需要的是用户信息,那么就把User类传入泛型中;
操作的对象有了,接下来就是使用:
在该项目中个人使用的地方在登录拦截器中,当对登录的信息检查成功后,那么将当前的用户对象加入到ThreadLocal中,
//登录验证成功,放行
System.out.println("走到UserThreadLocal--------");
UserThreadLocal.put(sysUser);
在controller中使用的时候,直接调用ThreadLocal中的get方法,就可以获得当前用户的信息:
//获取当前在线用户信息
SysUser sysUser = UserThreadLocal.get();
资源调用完成后需要在拦截器中删除ThreadLocal资源:
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//使用完的用户信息需要删除,防止内存泄露
UserThreadLocal.remove();
}
afterCompletion作用:
实现最终的完成后进行处理,该方法在执行完控制器之后执行,由于是在Controller方法执行完毕后执行该方法,所以该方法适合进行一些资源清理,记录日志信息等处理操作
什么意思呢:
程序首先执行拦截器类中的preHandle()方法,如果该方法返回值是true,则程序会继续向下执行处理器中的方法,否则不再向下执行;在业务控制器类Controller(这里就可以用ThreadLocal 获取用户信息)处理完请求后,会执行postHandle()方法,
而后会通过DispatcherServlet向客户端返回响应;在DispatcherServlet处理完请求后,才会执行afterCompletion()方法(这里删除这次请求的ThreadLocal 中的user信息);
ThreadLocal的内存泄露问题:
如果我们在使用完该线程后不进行ThreadLocal 中的变量进行删除,那么就会造成内存泄漏的问题,那么该问题是怎么出现的?
首先先看一下ThreadLocal 内部结构:
首先对于对象在栈中保存的是对象的引用,对象的存储在堆中,要先明确概念。栈中的格式也符合我们上述我们画的图1,其中Heap中的map是ThreadLocal map,里面包含key和value,其中value就是我们需要保存的变量数据,key则是ThreadLocal实例;
即:每一个Thread维护一个ThreadLocalMap, key为使用弱引用的ThreadLocal实例,value为线程变量的副本。
还需要注意的是上述图片的连接有实线和虚线,实线代表强引用,虚线表示弱引用;
扫盲强引用、软引用、弱引用、虚引用:
- 强引用,我们使用的大部分引用都是强引用,特点就是当内存不足时,垃圾回收器宁愿自己抛出OOM,也不会回收强引用来解决内存不足的问题
- 软引用,就是生杀大权都在垃圾回收器中,我内存够的话,你可以活着,如果不够的话,我就回收你,给我腾地方;
- 弱引用,只要垃圾回收器扫到,不管空间够不够,都给我回收了;
- 虚引用,形同虚设的东西,在任何情况下都可能被回收
这里只给出了概念,如果感兴趣可以自行了解作用环境。
那么知道强引用、弱引用后我们再来看图,因为ThreadLocal与线程属于同一个生命周期,当在某一个阶段进行了一次GC,那么当前线程还在,但是ThreadLocal实例被回收了,也就是key没了,
我们都知道,map中的value需要key找到,key没了,那么value就会永远的留在内存中,直到内存满了,导致OOM,所以我们就需要使用完以后进行手动删除,这样能保证不会出现因为GC的原因造成的OOM问题;
参考文献:
写的太细了!Spring MVC拦截器的应用,建议收藏再看!
结束:
如果你看到这里或者正好对你有所帮助,希望能点个或者感谢;
有错误的地方,欢迎在评论指出,作者看到会进行修改。
ThreadLocal概念以及使用场景的更多相关文章
- Android线程管理之ThreadLocal理解及应用场景
前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...
- MQ(1)---消息队列概念和使用场景
消息队列概念和使用场景 声明:本文转自:MQ入门总结(一)消息队列概念和使用场景 写的很好,都不用自己在整理了,非常感谢该作者的用心. 一.什么是消息队列 消息即是信息的载体.为了让消息发送者和消息接 ...
- kubernetes1.9管中窥豹-CRD概念、使用场景及实例
欢迎访问网易云社区,了解更多网易技术产品运营经验. 前言 默认读者有kubernetes基础概念的背景知识,因此基础概念例如有状态.pod.Replica Sets.Deployments.state ...
- [原创]cocos2d-x研习录-第二阶 概念类之场景类(CCScene)
场景类CCScene是Cocos2D-x在屏幕显示的内容,相当于游戏关卡或界面.CCDirector任何时候只能显示一个场景CCScene,游戏中可能存在若干场景,CCDirector通过场景切换达到 ...
- WCF回顾一、基本概念和应用场景
一.WCF描述 wcf是一款基于面向服务的架构的通讯框架平台,在分布式框架中得到了广泛使用. wcf入门非常简单,只要花几分钟就能编写一个完整的wcf程序,而实际上WCF是概念非常多的一门技术,需要花 ...
- 聊聊ThreadLocal原理以及使用场景-JAVA 8源码
相信很多人知道ThreadLocal是针对每个线程的,但是其中的原理相信大家不是很清楚,那咱们就一块看一下源码. 首先,我们先看看它的set方法.非常简单,从当前Thread中获取map.那么这个ge ...
- ThreadLocal 原理和使用场景分析
ThreadLocal 不知道大家有没有用过,但至少听说过,今天主要记录一下 ThreadLocal 的原理和使用场景. 使用场景 直接定位到 ThreadLocal 的源码,可以看到源码注释中有很清 ...
- [转]RabbitMQ入门教程(概念,应用场景,安装,使用)
原文地址:https://www.jianshu.com/p/dae5bbed39b1 RabbitMQ 简介 RabbitMQ是一个在AMQP(Advanced Message Queuing Pr ...
- redis(redis概念,运用场景,如何操作基本数据类型)
什么是redis:Nosql一种缓存数据库 redis可以干什么:redis可以减轻对数据库的请求压力如果不使用缓存:客服端->控制层->业务层->dao层使用缓存:客服端-> ...
随机推荐
- etcd学习(7)-etcd中的线性一致性实现
线性一致性 CAP 什么是CAP CAP的权衡 AP wihtout C CA without P CP without A 线性一致性 etcd中如何实现线性一致性 线性一致性写 线性一致性读 1. ...
- 新东方集团K12公益免费课战役记
作者:张建鑫, 曾任IBM高级软件架构师, 滴滴高级技术专家, 现任新东方集团高级技术总监 1月31日,集团领导决定由产品技术中心的新东方APP团队牵头做周一到周五的集团公益课, 提供给全国中小学生使 ...
- Selenium系列(十九) - Web UI 自动化基础实战(6)
如果你还想从头学起Selenium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1680176.html 其次,如果你不懂前端基础知识, ...
- Appium问题解决方案(10)- Original error: Swipe did not complete successfully
背景 从搜索页面返回首页之后,执行 swipe 滑动操作,但是报错了,如上图 解决方法 只需要在第一次 swipe 之前加个 sleep,强制等待即可 备注 这种解决方案其实不好,强制等待能少用就少 ...
- 机器学习——主成分分析(PCA)
1 前言 PCA(Principal Component Analysis)是一种常用的无监督学习方法,是一种常用的数据分析方法. PCA 通过利用 正交变换 把由 线性相关变量 表示的观测数据转换为 ...
- DFS与DFS迷宫问题
一天蒜头君掉进了一个迷宫里面,蒜头君想逃出去,可怜的蒜头君连迷宫是否有能逃出去的路都不知道. 看在蒜头君这么可怜的份上,就请聪明的你告诉蒜头君是否有可以逃出去的路. 输入格式 第一行输入两个整数n 和 ...
- Android学习记录(二)——第一次hello world及遇到的gradle安装问题
开始一个简单的hello world项目,简单了解Android studio的使用方法 第一步,打开Android studio,点击Create New Project 第二步,选择需要的模板 T ...
- 基于Typora的Latex代码书写并移植到word中
如何使用Markdown编译器 编辑Latex公式 并嵌入word内 前言:对于科研党来讲,在论文中数学公式的展示是必不可少的一环,但是如果不使用公式的格式去敲,那么公式就会过于难看,会大大降低你 ...
- 有备无患「GitHub 热点速览 v.21.38」
作者:HelloGitHub-小鱼干 数据库最重要的一个功能是容灾备份,备份不只是对数据库重要,对日常工作生活的我们一样重要,比如花了一个工作日写的代码没有备份(虽然可能只有 1 行-)总归是一个让人 ...
- 使用IntelliJ工具打包kotlin为bat文件运行报错 Exception in thread "main" java.lang.NoClassDefFoundError
Exception in thread "main" java.lang.NoClassDefFoundError 这个很有可能是因为idea里的java版本与电脑上的java环境 ...