点击关注公众号,实用技术文章及时了解

两大使用场景-ThreadLocal的用途

典型场景1: 每个线程需要一个独享的对象(通常是工具类,典型需要使用的类有SimpleDateFormat和Random)

典型场景2: 每个线程内需要保存全局变量(例如在拦截器中获取用户信息),可以让不同方法直接使用,避免参数传递的麻烦。

典型场景1:每个线程需要一个独享的对象

每个Thread内有自己的实例副本,不共享;

举例:SimpleDateFormat。(当多个线程共用这样一个SimpleDateFormat,但是这个类是不安全的)

  • 2个线程分别用自己的SimpleDateFormat,这没问题;

  • 后来延伸出10个,那就有10个线程和10个SimpleDateFormat,这虽然写法不优雅,但勉强可以接受

  • 但是当需求变成了1000,那么必然要用线程池,消耗内存太多;

  • 但是每一个SimpleDateFormat我们都需要创建一遍,那么太耗费new对象了,改成static共用的,所有线程都共用一个simpleDateFormat对象,但这是线程不安全的,容易出现时间一致的情况,在调用的时候,可加锁来解决,但还是不优雅;

  • 用ThreadLocal来解决该问题,给每个线程分配一个simpledateformat,可这个threadlocal是安全的;

package threadlocal;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 描述:     利用ThreadLocal,给每个线程分配自己的dateFormat对象,保证了线程安全,高效利用内存*/
public class ThreadLocalNormalUsage05 {public static ExecutorService threadPool = Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 1000; i++) {int finalI = i;threadPool.submit(new Runnable() {@Overridepublic void run() {String date = new ThreadLocalNormalUsage05().date(finalI);System.out.println(date);}});}threadPool.shutdown();}public String date(int seconds) {//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时Date date = new Date(1000 * seconds);
//        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal2.get();return dateFormat.format(date);}
}class ThreadSafeFormatter {public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {@Overrideprotected SimpleDateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");}};public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal2 = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}

典型场景2:当前用户信息需要被线程内所有方法共享

  • 一个比较繁琐的解决方案是把user作为参数层层传递,从service-1()传到service-2(),以此类推,但是这样做会导致代码冗余且不易维护。

  • 进阶点就是userMap来保存,但是当多线程同时工作时,需要保证线程安全,需要用synchronized,或者concurrenthashmap,但无论用什么,都会对性能有所影响

每个线程内需要保存全局变量,可以让不同方法直接使用,避免参数传递的麻烦

  • 用ThreadLocal保存一些业务内存(用户权限信息,从用户系统获取到的用户名、userId等)

  • 这些信息在同一个线程内相同,但是不同的线程使用的业务内容是不相同的

  • 在线程生命周期内,都通过这个静态ThreadLocal实例的get()方法取得自己set过的那个对象,避免了将这个对象作为参数传递的麻烦

package threadlocal;/*** 描述:     演示ThreadLocal用法2:避免传递参数的麻烦*/
public class ThreadLocalNormalUsage06 {public static void main(String[] args) {new Service1().process("");}
}class Service1 {public void process(String name) {User user = new User("超哥");UserContextHolder.holder.set(user);new Service2().process();}
}class Service2 {public void process() {User user = UserContextHolder.holder.get();ThreadSafeFormatter.dateFormatThreadLocal.get();System.out.println("Service2拿到用户名:" + user.name);new Service3().process();}
}class Service3 {public void process() {User user = UserContextHolder.holder.get();System.out.println("Service3拿到用户名:" + user.name);UserContextHolder.holder.remove();}
}class UserContextHolder {public static ThreadLocal<User> holder = new ThreadLocal<>();}class User {String name;public User(String name) {this.name = name;}
}

注意点:

  • 强调的是同一个请求内(同一个线程内)不同方法见的共享;

  • 不需重写initialValue()方法,但是必须手动调用set()方法

ThreadLocal方法使用总结

场景一:initialValue

在ThreadLocal第一次get的时候把对象给初始化出来,对象的初始化时机可以由我们控制。

场景二:set

如果需要保存到ThreadLocal里面的对象的生成时机不由我们随意控制。例如拦截器生成的用户信息,用ThreadLocal.set直接放到ThreadLocal当中。

ThreadLocal原理

理清Thread,ThreadLocalMap以及ThreadLocal

主要方法介绍

  • T initialValue(): 初始化

  • void set(T t): 为这个线程设置一新值

  • T get(): 得到这个线程对应的value。如果是首次调用get()。则会调用initialize来得到这个值

  • void remove(): 删除这个线程得到的值

ThreadLocalMap发生冲突之后,会用线性探测法。另外,欢迎关注公众号Java笔记虾,后台回复“后端面试”,送你一份面试题宝典!

ThreadLocal使用问题内存泄露

什么是内存泄露

某个对象不再有用,但是占用的内存却不能被回收。

Value的泄露

  • 在ThreadLocalMap中的每个Entry都是一个对key的弱引用,同时,每个Entry都包含了一个对value的强引用。

  • 正常情况 ,当线程终止,保存在ThreadLocal里的value会被垃圾回收,因为没有任何强引用了。

  • 但是,如果线程不终止(比如线程池需要保持很久),那么key对应的value就不能被回收。Thread->ThreadLocalMap->Entry(key为Null)->Value

  • 因为value和Thread之间还存在这个强引用链路,所以导致value无法回收,就可能出现OOM;JDK已经考虑到这个问题,所以在set,remove,rehash方法中会扫描key为null,会把value也设置为null,这样value对象就可以被回收了。

  • 但是如果一个ThreadLocal不被使用,那么实际上set,remove,rehash方法也不会被调用,如果同时线程又不停止,那么调用链就一直存在,那么就导致了value的内存泄露。

如何避免内存泄露呢?
  • 调用remove方法,就会删除对应的Entry对象,可以避免内存泄露,所以使用完ThreadLocal之后,应该调用remove方法。

实际应用场景-在spring中的实例分析

  • DateTimeContextHolder:用到了ThreadLocal

  • RequestContextHolder:用到了ThreadLocal

(感谢阅读,希望对你所有帮助)

来源:jblog.csdn.net/Mind_programmonkey/

article/details/118220731

●【练手项目】基于SpringBoot的ERP系统,自带进销存+财务+生产功能

●分享一套基于SpringBoot和Vue的企业级中后台开源项目,代码很规范!

●能挣钱的,开源 SpringBoot 商城系统,功能超全,超漂亮!

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!

京东一面:说出ThreadLocal的使用场景及使用方式相关推荐

  1. 京东一面:说说ThreadLocal的使用场景及使用方式

    来源 | 网络 正文 两大使用场景-ThreadLocal的用途 典型场景1: 每个线程需要一个独享的对象(通常是工具类,典型需要使用的类有SimpleDateFormat和Random) 典型场景2 ...

  2. 重组完成,京东金融走出京东:猛兽的进化

    重组完成,京东金融走出京东:猛兽的进化 陈纪英 百家号 08-15 00:20 连续五个季度盈利,单季度盈利接近去年全年,在亮眼的京东2017Q2财报里,也低调透了京东金融重组完成交割的消息.京东金融 ...

  3. 财报惊喜不断,京东彻底走出阴霾?

    7月22日,美国<财富>杂志公布了2019年世界500强排行榜,已经连续四年上榜的京东集团,在过去一年凭借稳扎稳打的零售业务和高速发展的服务业务,今年在榜单中的排位跃升至139位,较去年提 ...

  4. 多线程:什么是ThreadLocal?应用场景?

    ThreadLocal(避免线程不安全问题) 什么是ThreadLocal?  线程本地变量,也有些地方叫做线程本地存储,他代表一个线程局部变量. 为什么要ThreadLocal? 如果一段代码中所需 ...

  5. Fay控制器及数字人模型,可灵活组合出不同的应用场景:虚拟主播、现场推销货、商品导购、语音助理、远程语音助理、数字人互动、数字人面试官及心理测评、贾维斯、Her

    FAY 数 字 人 Fay 控 制 器(这是元宇宙吗?) Fay是一个完整的开源项目,包含Fay控制器及数字人模型,可灵活组合出不同的应用场景:虚拟主播.现场推销货.商品导购.语音助理.远程语音助理. ...

  6. 盲目出炉的支付场景 到底有多少是鸡肋?

    当下,花样百出的移动支付已经席卷了大众生活的方方面面.以手机.可穿戴设备等移动智能终端为基础,多个移动支付应用在疯狂地攻城略地,刷足了存在感.尤其是支付宝和微信支付之间的红包大战,彻底点燃了大众使用移 ...

  7. kafka 脚本发送_Kafka笔记归纳(第五部分:一致性保证,消息重复消费场景及解决方式)...

    写在开头: 本章是Kafka学习归纳第五部分,着重于强调Kafka的事一致性保证,消息重复消费场景及解决方式,记录偏移量的主题,延时队列的知识点. 文章内容输出来源:拉勾教育大数据高薪训练营. 一致性 ...

  8. 架构设计:负载均衡层设计方案(1)——负载场景和解决方式

    架构设计:负载均衡层设计方案(1)--负载场景和解决方式 1.不同的负载场景 我们知道负载均衡层的作用是"将来源于外部的处理压力通过某种规律/手段分摊到内部各个处理节点上",那么不 ...

  9. mysql如何根据业务分表设计_mysql分表分库的应用场景和设计方式

    很多朋友在论坛和留言区域问mysql在什么情况下才需要进行分库分表,以及采用何种设计方式才是最优的选择,根据这些问题,小编为大家整理了关于MySQL分库分表的应用场景和最优的设计方式举例. 一. 分表 ...

最新文章

  1. python列表的小东西_Python---列表相关操作
  2. 嵌入式环境:挂载开发板根NFS文件系统失败
  3. 天书夜读:WinDbg配置和使用基础
  4. 信息系统项目管理师考试公式都在这里了
  5. 学习计划2016//12/12
  6. Python pip切换为国内镜像源(亲测可用)
  7. OJ1038: 绝对值最大
  8. linux的nohup命令
  9. redis 首次请求_Redis主从复制
  10. Java 时断时续之————Java事件
  11. css实现文字太长,显示省略号
  12. 2020 Pwn2Own东京大赛落幕,Master of Pwn 诞生
  13. (三)svn 服务器端之创建仓库
  14. 【MDVRP】基于matlab遗传算法求解带距离的多车场车辆路径规划问题(含单线路局部优化)【含Matlab源码 1170期】
  15. 19深度探秘搜索技术_基于slop参数实现近似匹配以及原理剖析和相关实验
  16. 一个程序员应该怎样去学习和掌握计算机英语呢
  17. 针对开放平台的架构理解
  18. java毕业设计办公自动化管理系统Mybatis+系统+数据库+调试部署
  19. 虚幻4游戏引擎的安装
  20. 快递鸟电子面单打印接口demo-可返回电子面单模板

热门文章

  1. 支付宝信用卡还款也开始收费了!但还是比微信便宜2元...
  2. 大型翻车现场?人人车官博辟谣破产传闻 却被群嘲官博怕是还蒙在鼓里
  3. 输出一串小于100的随机数(rand())
  4. tokengetall php,token_get_all Split given source into PHP tokens php函数分享
  5. Java程序运行时,没有赋值的基本类型变量会在内存中分配空间吗?
  6. Oracle的数据字典常用操作(持续更新)
  7. foundation-datepicker只能选年份_你喝的年份酒和原浆酒都怎么来的?
  8. 403 forbidden nginx_linux搭建nginx服务
  9. 【算法】剑指 Offer 17. 打印从1到最大的n位数
  10. 【Flink】FlinkPer-partition watermark 问题 某个 分区延迟 导致数据丢失