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. Linux中3个文件查找相关命令详解

    源于:https://mp.weixin.qq.com/s/VPs-IXY6RoxbltHIxtIbng which命令 我们经常在linux要查找某个文件,但不知道放在哪里了,可以使用下面的一些命令 ...

  2. LVS负载均衡群集部署——DR模式

    LVS负载均衡群集部署--DR模式 1.LVS-DR概述 2.部署实验 1.LVS-DR概述: LVS-DR(Linux Virtual Server Director Server)工作模式,是生产 ...

  3. web安全知识拓扑

  4. serverless入门介绍

    1.什么是serverless Serverless 架构作为一种新型的云计算范式,是云原生时代一种革命性的架构,颠覆了传统意义上对软件应用部署和运营的认识.本节对 Serverless 架构的基本概 ...

  5. Solution -「CF 1349D」Slime and Biscuits

    \(\mathcal{Description}\)   Link.   有 \(n\) 堆饼干,一开始第 \(i\) 堆有 \(a_i\) 块.每次操作从所有饼干中随机一块,将其随机丢到另外一堆.求所 ...

  6. 通过shell脚本进行linux服务器的CPU和内存压测

    文章目录 内存压测 python的方式 shell的方式 cpu压测 在正常手段下,这个只是压测的方法 在不正常手段下(crontab计划任务),可以提高CPU和内存的使用率 什么?你问我为什么要提高 ...

  7. 我们一起来学Shell - 初识shell

    文章目录 Shell 的分类 `bash` `csh` `ksh` `tcsh` `sh` `nologin` `zsh` Shell 能做什么 bash 环境变量文件 `/etc/profile` ...

  8. 解决POI多线程导出时数据错乱问题

    项目里有一个导出功能,但随着数据量大量上涨,导出时间长到不可忍受,遂重写此接口,多线程导出的代码并不复杂,每页有一条线程负责写入,利用线程池去调度,用countdownLatch保证在所有数据写完后再 ...

  9. 关于iOS APP转让

    需要以下条件即可

  10. Linux 下 Git版本升级

    一.下载需要安装的版本号 wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.24.0.tar.gz 二.安装需求 yum ...