ThreadLocal的用处

ThreadLocal是一个多线程的辅助工具类,目的是方便开发者维护多线程中的共享变量。我们知道如果我们想要在一个线程中一直访问一个变量或者在线程上下文中保存一个变量,我们要么将该变量声明为static静态,要么就在每一步函数调用中均传入该变量。这两种方式,static方式不能解决每个线程同时分别持有的问题。每一步函数传入对代码的侵入性过高。

所以ThreadLocal实际的使用效果就是以线程维度,让每个线程拥有了一个自己的上下文变量池,防止大量的静态声明。这也是为什么它并不在juc并发包里的原因。

ThreadLocal的实现

通过上述TheadLocal的用处和它解决的问题,我们可以马上想到一个很简单的实现来补充static实现的缺点:我们可以通过定义一个static的Map,其中key为线程或线程id,value为需要保存的变量。

TheadLocal的实际实现方法也类似,不过在其中进行了一定改进:

  1. TheadLocal实现体系中,由Thead本身持有该Map,其中Key为TheadLocal,value为值。
  2. TheadLocal本身仅通过同包可以访问的特性,提供了一系列访问Thead中的Map的Api。

ThreadLocal防止内存泄露

我们知道,TheadLocal这种实现体系下,Thread会持有一个Map,并且该Map中会大量使用TheadLocal作为key,Map引用关系默认是强引用。在线程池或连接池这种会对线程进行复用的场景下,为了防止TheadLocal被强引用导致一直不能被回收,所以Thead中对TheadLocal的维护Map使用的是一个简单实现的WeakReferenceMap。该Map有以下特点:

  1. 该Map的哈希冲突使用跳跃式处理,而不是HashMap中的桶。原因应该是把该空间当做栈来思考的话,使用跳跃式可以减少更多的空间分配,并且更好的利用cpu缓存。
  2. 该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及其原理的更多相关文章

  1. 【原理】Java的ThreadLocal实现原理浅读

    当前线程的值传递,ThreadLocal 通过ThreadLocal设值,在线程内可获取,即时获取值时在其它Class或其它Method. public class BasicUsage { priv ...

  2. Java基础之Synchronized原理

    思维导图svg: https://note.youdao.com/ynoteshare1/index.html?id=eb05fdceddd07759b8b82c5b9094021a&type ...

  3. 【Java基础】HashMap原理详解

    哈希表(hash table) 也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,本文会对java集合框架中Has ...

  4. java基础-注解Annotation原理和用法

    在很多java代码中都可以看到诸如@Override.@Deprecated.@SuppressWarnings这样的字符,这些就是注解Annotation.注解最早在jdk5中被引入,现在已经成为j ...

  5. Java基础--ThreadLocal

    Java中的ThreadLocal 可以看做以线程标识为key的Map,在多线程开发中使用非常方便. 示例 class ThreadEnv { // 用匿名内部类覆盖ThreadLocal的initi ...

  6. Java基础 | Stream流原理与用法总结

    Stream简化元素计算: 一.接口设计 从Java1.8开始提出了Stream流的概念,侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式:依旧先看核心接口的设计: BaseStream: ...

  7. JAVA基础之——Thrift原理及应用

    1 是什么 是为了解决facebook系统中各系统间大数据量的传输通信,以及系统之间语言环境不同需要跨平台的问题. 是一种实现RPC的软件框架,自定义IDL(Interface description ...

  8. Java基础之Volatile原理

    原文链接: http://www.aoaoyi.com/archives/956.html 计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据 的读取和写入.由于程序运 ...

  9. Java基础之HashMap原理分析(put、get、resize)

    在分析HashMap之前,先看下图,理解一下HashMap的结构 我手画了一个图,简单描述一下HashMap的结构,数组+链表构成一个HashMap,当我们调用put方法的时候增加一个新的 key-v ...

  10. Java基础之LinkedHashMap原理分析

    知识准备HashMap 我们平时用LinkedHashMap的时候,都会写下面这段 LinkedHashMap<String, Object> map = new LinkedHashMa ...

随机推荐

  1. Android图表库hellocharts详解

    感谢大佬:https://www.cnblogs.com/huolongluo/p/5988644.html 因为项目需要搞一个折线图,按照日期显示相应的成绩,所以有了本文. 以前用过一次XCL-ch ...

  2. centOs编译安装php7.2支持微擎php扩展

    发现yum安装许多坑 于是只好编译安装 第一步得到镜像地址 在      https://www.php.net/downloads.php     有的地址比较慢,需要耐心等待 cd /usr/lo ...

  3. swift语言学习博文精选

    初探swift语言的学习笔记九(OC与Swift混编) Objective-C 与 Swift 混编之路 Swift项目兼容Objective-c问题汇总

  4. IP地址与子网划分

    IP地址与子网划分 目录 IP地址与子网划分 一.IP地址(Internet Protocol Address) 1.IP地址的表示 2.IP地址的组成 3.IP地址的分类 (1)A类IP地址 (2) ...

  5. JAVA String介绍、常量池及String、StringBuilder和StringBuffer得区别. 以及8种基本类型的包装类和常量池得简单介绍

    一.概述 String是代表字符串的类,本身是一个最终类,使用final修饰,不能被继承. 二.定义方式   方式一:直接赋值法 String str1 = "hello"; 方式 ...

  6. springcloud+gateway微服务整合swagger

    单一的微服务集成swagger: maven: <dependency> <groupId>io.springfox</groupId> <artifactI ...

  7. MATLAB基础学习篇(1)

    MATLAB中只定义了以2和10为底对数,其它 使用换底公式,例如:log8(7)=log7/log8. x=input('Please enter x:'); y=input('Please ent ...

  8. 《手把手教你》系列技巧篇(六十七)-java+ selenium自动化测试 - 读写excel文件 - 中篇(详细教程)

    1.简介 前面介绍了POI可以操作excel,也简单的提到另一个操作excle的工具,本篇介绍一个其他的可以操作excel的工具,但是这个工具有一个前提,excel文件版本只能是97-2003版本,如 ...

  9. 基于6U CPCIe的TMS320C6678+KU060的信号处理板卡

    一.产品概述 基于6U CPCIe的C6678+KU060的信号处理板卡是新一代FPGA的高性能处理板卡.板卡采用一片TI DSP TMS320C6678和一片Xilinx公司 XCKU060-2FF ...

  10. Solution -「BZOJ 4316」小C的独立集

    \(\mathcal{Description}\)   Link.   求包含 \(n\) 个结点 \(m\) 条边的仙人掌的最大独立集.   \(n\le5\times10^4\),\(m\le6\ ...