手撕面试题ThreadLocal!!!
说明
面试官:讲讲你对ThreadLocal的一些理解。
那么我们该怎么回答呢????你也可以思考下,下面看看零度的思考;
ThreadLocal用在什么地方?
ThreadLocal一些细节!
ThreadLocal的最佳实践!
思考
ThreadLocal用在什么地方?
讨论ThreadLocal用在什么地方前,我们先明确下,如果仅仅就一个线程,那么都不用谈ThreadLocal的,ThreadLocal是用在多线程的场景的!!!
ThreadLocal归纳下来就2类用途:
- 保存线程上下文信息,在任意需要的地方可以获取!!!
- 线程安全的,避免某些情况需要考虑线程安全必须同步带来的性能损失!!!
保存线程上下文信息,在任意需要的地方可以获取!!!
由于ThreadLocal的特性,同一线程在某地方进行设置,在随后的任意地方都可以获取到。从而可以用来保存线程上下文信息。
常用的比如每个请求怎么把一串后续关联起来,就可以用ThreadLocal进行set,在后续的任意需要记录日志的方法里面进行get获取到请求id,从而把整个请求串起来。
还有比如Spring的事务管理,用ThreadLocal存储Connection,从而各个DAO可以获取同一Connection,可以进行事务回滚,提交等操作。
备注: ThreadLocal的这种用处,很多时候是用在一些优秀的框架里面的,一般我们很少接触,反而下面的场景我们接触的更多一些!
线程安全的,避免某些情况需要考虑线程安全必须同步带来的性能损失!!!
ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。但是ThreadLocal也有局限性,我们来看看阿里规范:

每个线程往ThreadLocal中读写数据是线程隔离,互相之间不会影响的,所以ThreadLocal无法解决共享对象的更新问题!
由于不需要共享信息,自然就不存在竞争问题了,从而保证了某些情况下线程的安全,以及避免了某些情况需要考虑线程安全必须同步带来的性能损失!!!
这类场景阿里规范里面也提到了:

ThreadLocal一些细节!
ThreaLocal使用示例代码:
public class ThreadLocalTest {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
threadLocal.set(i);
System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get());
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
threadLocal.remove();
}
}, "threadLocal1").start();
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get());
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
threadLocal.remove();
}
}, "threadLocal2").start();
}
}
代码截图:

代码运行结果:

从运行的结果我们可以看到threadLocal1进行set值对threadLocal2并没有任何影响!
Thread、ThreadLocalMap、ThreadLocal总览图


Thread类有属性变量threadLocals (类型是ThreadLocal.ThreadLocalMap),也就是说每个线程有一个自己的ThreadLocalMap ,所以每个线程往这个ThreadLocal中读写隔离的,并且是互相不会影响的。
一个ThreadLocal只能存储一个Object对象,如果需要存储多个Object对象那么就需要多个ThreadLocal!!!
如图:

看到上面的几个图,大概思路应该都清晰了,我们Entry的key指向ThreadLocal用虚线表示弱引用 ,下面我们来看看ThreadLocalMap:

java对象的引用包括 : 强引用,软引用,弱引用,虚引用 。
因为这里涉及到弱引用,简单说明下:
弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,该对象仅仅被弱引用关联,那么就会被回收。
当仅仅只有ThreadLocalMap中的Entry的key指向ThreadLocal的时候,ThreadLocal会进行回收的!!!
ThreadLocal被垃圾回收后,在ThreadLocalMap里对应的Entry的键值会变成null,但是Entry是强引用,那么Entry里面存储的Object,并没有办法进行回收,所以ThreadLocalMap 做了一些额外的回收工作。

虽然做了但是也会存在内存泄漏风险(我没有遇到过,网上很多类似场景,所以会提到后面的ThreadLocal最佳实践!!!)
ThreadLocal的最佳实践!
ThreadLocal被垃圾回收后,在ThreadLocalMap里对应的Entry的键值会变成null,但是Entry是强引用,那么Entry里面存储的Object,并没有办法进行回收,所以ThreadLocalMap 做了一些额外的回收工作。

备注: 很多时候,我们都是用在线程池的场景,程序不停止,线程基本不会销毁!!!
由于线程的生命周期很长,如果我们往ThreadLocal里面set了很大很大的Object对象,虽然set、get等等方法在特定的条件会调用进行额外的清理,但是ThreadLocal被垃圾回收后,在ThreadLocalMap里对应的Entry的键值会变成null,但是后续在也没有操作set、get等方法了。
所以最佳实践,应该在我们不使用的时候,主动调用remove方法进行清理。

这里把ThreadLocal定义为static还有一个好处就是,由于ThreadLocal有强引用在,那么在ThreadLocalMap里对应的Entry的键会永远存在,那么执行remove的时候就可以正确进行定位到并且删除!!!
最佳实践做法应该为:
try {
// 其它业务逻辑
} finally {
threadLocal对象.remove();
}

思考
如果面试的时候,可以把上面的内容都可以讲到,个人觉得就非常好了,回答的就挺完美了。但是如果你可以进行下面的回答,那么就更完美了。
对于ThreadLocal,我在看Netty源码的时候,还了解过FastThreadLocal,xxxxx一些列内容,那就是一个升级了。

在我本地进行测试,FastThreadLocal的吞吐量是jdkThreadLocal的3倍左右。
备注: 由于FastThreadLocal内容也非常非常多,而且有很多技巧,所以准备后续专门在开一篇进行串起来!!!
**如果读完觉得有收获的话,欢迎点赞、关注、加公众号 [匠心零度] ,查阅更多精彩历史!!! **

手撕面试题ThreadLocal!!!的更多相关文章
- Netty实现高性能IOT服务器(Groza)之手撕MQTT协议篇上
前言 诞生及优势 MQTT由Andy Stanford-Clark(IBM)和Arlen Nipper(Eurotech,现为Cirrus Link)于1999年开发,用于监测穿越沙漠的石油管道.目标 ...
- 手撕RPC框架
手撕RPC 使用Netty+Zookeeper+Spring实现简易的RPC框架.阅读本文需要有一些Netty使用基础. 服务信息在网络传输,需要讲服务类进行序列化,服务端使用Spring作为容器.服 ...
- NN入门,手把手教你用Numpy手撕NN(一)
前言 这是一篇包含极少数学推导的NN入门文章 大概从今年4月份起就想着学一学NN,但是无奈平时时间不多,而且空闲时间都拿去做比赛或是看动漫去了,所以一拖再拖,直到这8月份才正式开始NN的学习. 这篇文 ...
- NN入门,手把手教你用Numpy手撕NN(2)
这是一篇包含较少数学推导的NN入门文章 上篇文章中简单介绍了如何手撕一个NN,但其中仍有可以改进的地方,将在这篇文章中进行完善. 误差反向传播 之前的NN计算梯度是利用数值微分法,虽容易实现,但是计算 ...
- 手撕公司SSO登陆原理
Single Sign-on SSO是老生常谈的话题了,但部分同学对SSO可能掌握的也是云里雾里,一知半解.本次手撕公司的SSO登陆原理,试图以一种简单,流畅的形式为你提供 有用的SSO登陆原理. 按 ...
- NN入门,手把手教你用Numpy手撕NN(三)
NN入门,手把手教你用Numpy手撕NN(3) 这是一篇包含极少数学的CNN入门文章 上篇文章中简单介绍了NN的反向传播,并利用反向传播实现了一个简单的NN,在这篇文章中将介绍一下CNN. CNN C ...
- 手撕代码:统计1到n二进制数中1出现的总次数
题目描述: 互娱手撕代码题. 统计从1到n这n个数的二进制表示中1出现的次数. 思路分析: 思路一:直接的做法是从1遍历到n,对于每个数和1做与操作,之后,对于这个数不断做右移操作,不断和1做与操作, ...
- 手撕ES6--Promise
手撕ES6--Promise:https://www.jianshu.com/p/0925eae38d2c 手写一个Promise,附源码分析:https://blog.csdn.net/weixin ...
- 几道JS代码手写面试题
几道JS代码手写面试题 (1) 高阶段函数实现AOP(面向切面编程) Function.prototype.before = function (beforefn) { let ...
随机推荐
- SQL Server 2005高可用性模式下创建数据库镜像
SQL Server 2005高可用性模式下创建数据库镜像 高可用性模式下创建数据库镜像 第一步: --创建镜像用数据库-在主服务器上操作 create database db_mirror on ...
- apache-maven-3.0.4-bin.zip
http://zhidao.baidu.com/share/2a8974fd1546ef5f11ad9cccb3cabf88.html apache-maven-3.0.4-bin.zip
- tomcat加载web.xml
这几天看tomcat的源码,疑问很多,比如之一“ tomcat 怎么加载 web.xml”,下面是跟踪的过程,其中事件监听器有一个观察者模式,比较好.记录下来以供参考 >>>> ...
- CetnOS6 网络配置,主机名配置
CetnOS6 网络配置,主机名配置 一.通过命令ifconfig -a 查看可用网络设备 二.通过网络配置文件/etc/sysconfig/network-scripts/ifcfg-eth0 配置 ...
- vi命令常用操作
一.vi的操作模式 vi提供两种操作模式:输入模式(insert mode)和指令模式(command mode).在输入模式下,用户可输入文本资料.在指令模式下,可进行删除.修改等各种编辑动作. ...
- Flex XML/XMLList 常用操作
1 XML.XMLList操作 Flex对xml提供了很多强大而灵活的操作.相对于其他语言,flex对xml的格式要求不那么苛刻,只要符合基本格式语法的字符串,flex能非常简单的转换成x ...
- XTUOJ 1238 Segment Tree
Segment Tree Accepted : 3 Submit : 21Time Limit : 9000 MS Memory Limit : 65536 KB Problem Descriptio ...
- xadmin列表页添加自定义工具栏toolbar
通过xadmin的Plugin实现,adminx.py中 class Link2AdminPlugin(BaseAdminPlugin): link_2_admin = False def init_ ...
- jsp&servlet报红线javax.servlet.jsp.XXXX cannot be resolved to a type类似错误解决办法
javax.servlet.jsp.JspException cannot be resolved to a type javax.servlet.jsp.PageContext cannot be ...
- 洛谷P3531 [POI2012]LIT-Letters
题目描述 Little Johnny has a very long surname. Yet he is not the only such person in his milieu. As it ...