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 ...
随机推荐
- Java线程--BlockingQueue使用
原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11871704.html Java线程--BlockingQueue使用 阻塞队列就是内容满了之 ...
- NS前缀
NS来自于NeXTStep的一个软件 NeXT Software OC中不支持命名空间(namespace) NS是为了避免命名冲突而给的前缀 看到NS前缀就知道是Cocoa中的系统类的名称
- 微服务技术栈简单介绍,Eureka和Ribbon的引入和使用
一.了解微服务架构 1.微服务技术栈 整体框架 整体学习规划路线2.微服务与单体架构的区别 单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署 优势 结构简单 部署成本低 缺点 耦合度高, ...
- 记录netcore一次内存暴涨的坑
项目用到了Coldairarrow/EFCore.Sharding: Database Sharding For EFCore (github.com)这个组件,最初是因为分表做的还不错所以用了它. ...
- 虫师Selenium2+Python_2、测试环境搭建
windows环境配置: 步骤: 安装python 官网下载http://www.seleniumhq.org/ https://www.python.org/downloads/windows/ 3 ...
- 帆软报表(finereport) 组合地图 保持系列名和值居中
自定义JavaScript代码,使用HTML解析 function(){ var name = this.name; var total = '<div style="width:10 ...
- [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (2)
[源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (2) 目录 [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (2) 0x00 摘要 0x01 总体流程 ...
- 我们一起来学Shell - 初识shell
文章目录 Shell 的分类 `bash` `csh` `ksh` `tcsh` `sh` `nologin` `zsh` Shell 能做什么 bash 环境变量文件 `/etc/profile` ...
- Dubbo基础二之架构及处理流程概述
Dubbo基础一之实战初体验 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中体验了Dubbo的使用,对于消费端对服务提供者的调用非常清晰明确.那么Dubbo是如何做到的呢?下面对Dub ...
- 使用Jitpack发布开源Java库
原文:使用Jitpack发布开源Java库 | Stars-One的杂货小窝 很久之前也写过一篇使用Jitpack发布Android开源库的文章,详见Android开发--发布第三方库到JitPack ...