Java基础:ThreadLocal及其原理
ThreadLocal的用处
ThreadLocal是一个多线程的辅助工具类,目的是方便开发者维护多线程中的共享变量。我们知道如果我们想要在一个线程中一直访问一个变量或者在线程上下文中保存一个变量,我们要么将该变量声明为static静态,要么就在每一步函数调用中均传入该变量。这两种方式,static方式不能解决每个线程同时分别持有的问题。每一步函数传入对代码的侵入性过高。
所以ThreadLocal实际的使用效果就是以线程维度,让每个线程拥有了一个自己的上下文变量池,防止大量的静态声明。这也是为什么它并不在juc并发包里的原因。
ThreadLocal的实现
通过上述TheadLocal的用处和它解决的问题,我们可以马上想到一个很简单的实现来补充static实现的缺点:我们可以通过定义一个static的Map,其中key为线程或线程id,value为需要保存的变量。
TheadLocal的实际实现方法也类似,不过在其中进行了一定改进:
- TheadLocal实现体系中,由Thead本身持有该Map,其中Key为TheadLocal,value为值。
- TheadLocal本身仅通过同包可以访问的特性,提供了一系列访问Thead中的Map的Api。
ThreadLocal防止内存泄露
我们知道,TheadLocal这种实现体系下,Thread会持有一个Map,并且该Map中会大量使用TheadLocal作为key,Map引用关系默认是强引用。在线程池或连接池这种会对线程进行复用的场景下,为了防止TheadLocal被强引用导致一直不能被回收,所以Thead中对TheadLocal的维护Map使用的是一个简单实现的WeakReferenceMap。该Map有以下特点:
- 该Map的哈希冲突使用跳跃式处理,而不是HashMap中的桶。原因应该是把该空间当做栈来思考的话,使用跳跃式可以减少更多的空间分配,并且更好的利用cpu缓存。
- 该Map的Entry,由继承了WeakReference的Entry实现,该Entry本身为TheadLocal的一个弱引用,当TheadLocal的强引用失去,被gc回收后。该Map在其各方法中都增加了清理Map节点的步骤,根据该弱引用对应的弱引用回收队列中的节点,清理Map中对应的键值对。
ThreadLocal为什么会内存泄露
这点其实应该放在上面那点之前说,但是这个确实也困扰了比较久,钻了牛角尖,一直没有明白。
我们日常中一般使用TheadLocal的方式都如下,比如使用一个Util类来维护请求的TraceId,进行链路追踪的效果:
public class TraceIDUtils {
private static final ThreadLocal<String> SEQUENCE_ID = new ThreadLocal<String>();
}
那么我们会发现,该TheadLocal对象,本身就是一个static声明,永远存在来源于该类的强引用,那么该TheadLocal本身就不会被回收,Thead中的Map是否使用弱引用好像毫无意义。
当时也是思考了比较久,后面才发现钻了牛角尖,当时大牛们设计出来,应该是考虑类似情况:
public class Test {
ThreadLocal<String> threadLocal;
public void run(){
this.threadLocal = new ThreadLocal<>();
this.threadLocal.set("abc");
//do something
//……
//end
}
public static void main(String[] args) throws Exception {
new Test().run();
}
}
我们现在当然知道,不使用static定义,肯定是不合适的,但是Java的大牛们作为Api的设计者,肯定是要考虑到其他人在任何情况下使用该工具都应是安全的。而在这种使用方式下,在线程被销毁前,显然如果线程中的Map对该TheadLocal为强引用,那么在该方法执行完毕后该方法内的强引用消失后,依旧在Map上会直接造成内存泄露。
这也能使人明白为什么Java不选择在TheadLocal中维护一个以Thead为Key的Map来实现。
Java基础:ThreadLocal及其原理的更多相关文章
- 【原理】Java的ThreadLocal实现原理浅读
当前线程的值传递,ThreadLocal 通过ThreadLocal设值,在线程内可获取,即时获取值时在其它Class或其它Method. public class BasicUsage { priv ...
- Java基础之Synchronized原理
思维导图svg: https://note.youdao.com/ynoteshare1/index.html?id=eb05fdceddd07759b8b82c5b9094021a&type ...
- 【Java基础】HashMap原理详解
哈希表(hash table) 也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,本文会对java集合框架中Has ...
- java基础-注解Annotation原理和用法
在很多java代码中都可以看到诸如@Override.@Deprecated.@SuppressWarnings这样的字符,这些就是注解Annotation.注解最早在jdk5中被引入,现在已经成为j ...
- Java基础--ThreadLocal
Java中的ThreadLocal 可以看做以线程标识为key的Map,在多线程开发中使用非常方便. 示例 class ThreadEnv { // 用匿名内部类覆盖ThreadLocal的initi ...
- Java基础 | Stream流原理与用法总结
Stream简化元素计算: 一.接口设计 从Java1.8开始提出了Stream流的概念,侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式:依旧先看核心接口的设计: BaseStream: ...
- JAVA基础之——Thrift原理及应用
1 是什么 是为了解决facebook系统中各系统间大数据量的传输通信,以及系统之间语言环境不同需要跨平台的问题. 是一种实现RPC的软件框架,自定义IDL(Interface description ...
- Java基础之Volatile原理
原文链接: http://www.aoaoyi.com/archives/956.html 计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据 的读取和写入.由于程序运 ...
- Java基础之HashMap原理分析(put、get、resize)
在分析HashMap之前,先看下图,理解一下HashMap的结构 我手画了一个图,简单描述一下HashMap的结构,数组+链表构成一个HashMap,当我们调用put方法的时候增加一个新的 key-v ...
- Java基础之LinkedHashMap原理分析
知识准备HashMap 我们平时用LinkedHashMap的时候,都会写下面这段 LinkedHashMap<String, Object> map = new LinkedHashMa ...
随机推荐
- VC 获取多个mac地址
转载请注明来源:https://www.cnblogs.com/hookjc/ #include <IPHlpApi.h>#include <iostream>#pragma ...
- kali切换桌面环境
感谢大佬:https://blog.csdn.net/tao546377318/article/details/52353018 kali 是基于Debian的发行版,兼容性和软件支持都很好,默认使用 ...
- 群聊(udp)
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import ...
- Docker私有仓库与Harbor部署使用
Docker私有仓库与Harbor部署使用 目录 Docker私有仓库与Harbor部署使用 一.本地私有仓库 1. 下载registry镜像 2. 在daemon.json文件中添加私有镜像仓库地址 ...
- python——虚拟环境管理大合集
个人常用:pipenv 安装 pip3 install pipenv 创建虚拟环境 # 默认安装在~/.local/virtualenv下 mkdir project cd project pipen ...
- 浅谈Java中重写与重载的区别
重载和重写是Java中两个截然不同的概念.但是却因为名字相近导致很多人经常混淆. 下面用例子展示出他们之间的区别. 在Java中,重载(overloading) 发生在本类.方法名相同,参数列表不同, ...
- Solution -「CF 855G」Harry Vs Voldemort
\(\mathcal{Description}\) Link. 给定一棵 \(n\) 个点的树和 \(q\) 次加边操作.求出每次操作后,满足 \(u,v,w\) 互不相等,路径 \((u,w ...
- Spring MVC拦截器(Interceptor)使用
第一篇Spring MVC的小作文就是关于Interceptor的,而不是很多基础的东西呢,很无奈.因为实践的项目中用到了,用地不太好,导致重复跳转页面浏览器cookie溢出了. 这个过程中呢就将与I ...
- (反射+内省机制的运用)简单模拟spring IoC容器的操作
简单模拟spring IoC容器的操作[管理对象的创建.管理对象的依赖关系,例如属性设置] 实体类Hello package com.shan.hello; public class Hello { ...
- linux系统开机流程
基本步骤:上电->bios->MBR引导->GRUB菜单->加载内核->运行init进程初始化->启动/etc/rc.d*脚本与相关配置文件->执行rc.lo ...