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为线程变量的副本。

还需要注意的是上述图片的连接有实线和虚线,实线代表强引用,虚线表示弱引用;

扫盲强引用、软引用、弱引用、虚引用:

  1. 强引用,我们使用的大部分引用都是强引用,特点就是当内存不足时,垃圾回收器宁愿自己抛出OOM,也不会回收强引用来解决内存不足的问题
  2. 软引用,就是生杀大权都在垃圾回收器中,我内存够的话,你可以活着,如果不够的话,我就回收你,给我腾地方;
  3. 弱引用,只要垃圾回收器扫到,不管空间够不够,都给我回收了;
  4. 虚引用,形同虚设的东西,在任何情况下都可能被回收

这里只给出了概念,如果感兴趣可以自行了解作用环境。

那么知道强引用、弱引用后我们再来看图,因为ThreadLocal与线程属于同一个生命周期,当在某一个阶段进行了一次GC,那么当前线程还在,但是ThreadLocal实例被回收了,也就是key没了,

我们都知道,map中的value需要key找到,key没了,那么value就会永远的留在内存中,直到内存满了,导致OOM,所以我们就需要使用完以后进行手动删除,这样能保证不会出现因为GC的原因造成的OOM问题;

参考文献:

ThreadLocal解决了什么问题

SpringMVC---拦截器

写的太细了!Spring MVC拦截器的应用,建议收藏再看!

结束:

如果你看到这里或者正好对你有所帮助,希望能点个或者感谢;

有错误的地方,欢迎在评论指出,作者看到会进行修改。

ThreadLocal概念以及使用场景的更多相关文章

  1. Android线程管理之ThreadLocal理解及应用场景

    前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...

  2. MQ(1)---消息队列概念和使用场景

    消息队列概念和使用场景 声明:本文转自:MQ入门总结(一)消息队列概念和使用场景 写的很好,都不用自己在整理了,非常感谢该作者的用心. 一.什么是消息队列 消息即是信息的载体.为了让消息发送者和消息接 ...

  3. kubernetes1.9管中窥豹-CRD概念、使用场景及实例

    欢迎访问网易云社区,了解更多网易技术产品运营经验. 前言 默认读者有kubernetes基础概念的背景知识,因此基础概念例如有状态.pod.Replica Sets.Deployments.state ...

  4. [原创]cocos2d-x研习录-第二阶 概念类之场景类(CCScene)

    场景类CCScene是Cocos2D-x在屏幕显示的内容,相当于游戏关卡或界面.CCDirector任何时候只能显示一个场景CCScene,游戏中可能存在若干场景,CCDirector通过场景切换达到 ...

  5. WCF回顾一、基本概念和应用场景

    一.WCF描述 wcf是一款基于面向服务的架构的通讯框架平台,在分布式框架中得到了广泛使用. wcf入门非常简单,只要花几分钟就能编写一个完整的wcf程序,而实际上WCF是概念非常多的一门技术,需要花 ...

  6. 聊聊ThreadLocal原理以及使用场景-JAVA 8源码

    相信很多人知道ThreadLocal是针对每个线程的,但是其中的原理相信大家不是很清楚,那咱们就一块看一下源码. 首先,我们先看看它的set方法.非常简单,从当前Thread中获取map.那么这个ge ...

  7. ThreadLocal 原理和使用场景分析

    ThreadLocal 不知道大家有没有用过,但至少听说过,今天主要记录一下 ThreadLocal 的原理和使用场景. 使用场景 直接定位到 ThreadLocal 的源码,可以看到源码注释中有很清 ...

  8. [转]RabbitMQ入门教程(概念,应用场景,安装,使用)

    原文地址:https://www.jianshu.com/p/dae5bbed39b1 RabbitMQ 简介 RabbitMQ是一个在AMQP(Advanced Message Queuing Pr ...

  9. redis(redis概念,运用场景,如何操作基本数据类型)

    什么是redis:Nosql一种缓存数据库 redis可以干什么:redis可以减轻对数据库的请求压力如果不使用缓存:客服端->控制层->业务层->dao层使用缓存:客服端-> ...

随机推荐

  1. etcd学习(7)-etcd中的线性一致性实现

    线性一致性 CAP 什么是CAP CAP的权衡 AP wihtout C CA without P CP without A 线性一致性 etcd中如何实现线性一致性 线性一致性写 线性一致性读 1. ...

  2. 新东方集团K12公益免费课战役记

    作者:张建鑫, 曾任IBM高级软件架构师, 滴滴高级技术专家, 现任新东方集团高级技术总监 1月31日,集团领导决定由产品技术中心的新东方APP团队牵头做周一到周五的集团公益课, 提供给全国中小学生使 ...

  3. Selenium系列(十九) - Web UI 自动化基础实战(6)

    如果你还想从头学起Selenium,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1680176.html 其次,如果你不懂前端基础知识, ...

  4. Appium问题解决方案(10)- Original error: Swipe did not complete successfully

    背景 从搜索页面返回首页之后,执行  swipe 滑动操作,但是报错了,如上图 解决方法 只需要在第一次 swipe 之前加个 sleep,强制等待即可 备注 这种解决方案其实不好,强制等待能少用就少 ...

  5. 机器学习——主成分分析(PCA)

    1 前言 PCA(Principal Component Analysis)是一种常用的无监督学习方法,是一种常用的数据分析方法. PCA 通过利用 正交变换 把由 线性相关变量 表示的观测数据转换为 ...

  6. DFS与DFS迷宫问题

    一天蒜头君掉进了一个迷宫里面,蒜头君想逃出去,可怜的蒜头君连迷宫是否有能逃出去的路都不知道. 看在蒜头君这么可怜的份上,就请聪明的你告诉蒜头君是否有可以逃出去的路. 输入格式 第一行输入两个整数n 和 ...

  7. Android学习记录(二)——第一次hello world及遇到的gradle安装问题

    开始一个简单的hello world项目,简单了解Android studio的使用方法 第一步,打开Android studio,点击Create New Project 第二步,选择需要的模板 T ...

  8. 基于Typora的Latex代码书写并移植到word中

    如何使用Markdown编译器 编辑Latex公式 并嵌入word内 ​ 前言:对于科研党来讲,在论文中数学公式的展示是必不可少的一环,但是如果不使用公式的格式去敲,那么公式就会过于难看,会大大降低你 ...

  9. 有备无患「GitHub 热点速览 v.21.38」

    作者:HelloGitHub-小鱼干 数据库最重要的一个功能是容灾备份,备份不只是对数据库重要,对日常工作生活的我们一样重要,比如花了一个工作日写的代码没有备份(虽然可能只有 1 行-)总归是一个让人 ...

  10. 使用IntelliJ工具打包kotlin为bat文件运行报错 Exception in thread "main" java.lang.NoClassDefFoundError

    Exception in thread "main" java.lang.NoClassDefFoundError 这个很有可能是因为idea里的java版本与电脑上的java环境 ...