Java面试之ThreadLocal及其内存泄露

java面试题22ThreadLocal会出现内存泄露么 ThreadLocal的内存泄漏风险及防范措施 ThreadLocal使用不当确实会导致内存泄漏,核心原因是其内部Entry对key(ThreadLocal实例)采用弱引用,而对value保持强引用。当ThreadLocal实例被回收后,Entry的key变为null,但value仍被强引用,若线程长期存活(如线程池),value将无法回收,引发内存泄漏。 防范措施: 必须调用remove():在finally块中显式清理,尤其在线程池场景; 规范声明:使用private static final修饰Thr 阅读详情

什么是ThreadLocal

ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离

ThreadLocal的简单使用

package com.mxx;

import java.util.concurrent.TimeUnit;

public class ThreadLocalTest {

    public static void main(String[] args) {
        ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
        new ThreadLocal<Integer>();
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(()->{
                threadLocal.set(finalI);
                // 暂停一秒。保证local值全部set完
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("线程"+Thread.currentThread().getName()+"\tlocal值="+threadLocal.get());

            },"线程"+i).start();
        }
    }
}
/*
线程线程0	local值=0
线程线程2	local值=2
线程线程3	local值=3
线程线程1	local值=1
线程线程4	local值=4
*/

由此可见,ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的

ThreadLocal应用场景

1、管理Connection数据库连接池

​ 实现当前线程操作的都是同一个Connection对象,保证了事务

2、避免参数传递,在线程内部任何地方都可以使用,存取方便

ThreadLocal内部原理

ThreadLocal set()方法

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

ThreadLocal实际使用ThreadLocalMap来存储变量

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    // ...
    
    private void set(ThreadLocal<?> key, Object value) {
			// ...
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
}

ThreadLocal 在set方法中使用getMap(t)获取ThreadLocalMap

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
}

ThreadLocalMap变量是Thread类维护的

ThreadLocal.ThreadLocalMap threadLocals = null;

1、set方法获取当前线程的ThreadLocalMap,将ThreadLocal对象本身作为key,要存的变量作为value存入map中

2、ThreadLocalMap是ThreadLocal的内部类,用Entry类来进行存储,但ThreadLocalMap对象的引用是在Thread中

3、总结:Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是ThreadLocal对象本身,value则是要存储的对象

ThreadLocal get(),同理

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

ThreadLocal原理总结

  1. 每个Thread维护着一个ThreadLocalMap的引用
  2. ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
  3. 调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象
  4. 调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象
  5. ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。

正因为这个原理,所以ThreadLocal能够实现“数据隔离”,获取当前线程的局部变量值,不受其他线程影响~

ThreadLocal之内存泄漏

简单区分:

  • 内存溢出:OOM
  • 内存泄露:无法GC

如何导致的?

ThreadLocal通过Entry保存变量,key为ThreadLocal的弱引用。

当ThreadLocal没有外部强引用时,此时系统GC就会导致ThreadLocal回收,因此Entry里key为null,无法访问value。

若当前线程不结束,value的强引用链就一直存在。Thread -> ThreadLocalMap -> Entry -> value,value无法回收,造成内存泄露。

因为ThreadLocalMap和Thead生命周期一样长,当前线程不结束,就不会回收。

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

为何使用弱引用?

1、假设ThreadLocal是强引用

此时若没有了外部引用,但ThreadLocal被ThreadLocalMap的Entry强引用,导致ThreadLocal无法被GC,存在以下强引用链:

Thread > ThreadLocalMap -> Entry -> TheadLocal

就算调用remove方法释放value,Entry 的key TheadLocal 也不会释放,因此导致Entry内存泄露

2、假设使用弱引用

ThreadLocal会被回收,value在调用remove时被回收。

如何避免?

1、用static修饰 ,static ThreadLocal,延长ThreadLocal生命周期,避免ThreadLocal强引用丢失而被回收

2、每次使用完ThreadLocal,都调用remove()方法,清除数据

参考:

https://juejin.im/post/5ac2eb52518825555e5e06ee

https://www.jianshu.com/p/1342a879f523

思科9117AP从零配置EWC模式实战:Wi-Fi6网络搭建避坑指南 本文详细介绍了思科9117AP在Wi-Fi6网络环境下配置EWC模式的实战指南,涵盖镜像准备、网络环境配置、模式转换步骤及高级运维技巧。通过EWC模式,企业可实现控制器与AP的一体化部署,显著降低成本和简化运维,特别适合中小企业Wi-Fi6网络搭建。 阅读详情

相关推荐

从零到一:RagFlow源码架构深度解析与二次开发实战指南

本文深入解析RagFlow源码架构,提供从环境搭建到二次开发的全流程实战指南。作为企业级AI搜索框架,RagFlow通过模块化设计和多模态处理能力,支持自定义智能体开发与文搜文档功能,帮助开发者高效构建智能搜索系统。

w1x2y3的博客 268

类ThreadLocal的使用

ThreadLocal的使用

weixin_37585619的博客 1380

DC算法竞赛——北京PM2.5浓度回归分析

北京PM2.5浓度回归分析 比赛结果(118/1150) 一. 概述 1、项目来源:https://www.dcjingsai.com/static_page/cmpList.html 2、给定数据字段如下: 3、使用方法:回归算法 4、目的:预测北京pm2.5值,使平均预测误差越小越好。 二、分析过程 setwd("E:/R数据源") #读入数据 data <- read.csv('p...

玻璃球的博客 3475

java ThreadLocal

每次看到这玩意熟悉,就是想不起来什么东西,看了才知道是啥玩意,就写个笔记强化一下记忆。 private ThreadLocal threadLocal = new ThreadLocal(); threadLocal.set(0); (int) threadLocal.get(); 上...

choucongyi6778的博客 295

操作系统原理:【优麒麟】linux内核添加系统调用

在底端添加如下代码。

Joyce_zz的博客 2711

统信UOS V20 安装mysql5.7.42详细教程

到bin/下,执行下面的命令,初始化mysql服务信息,得到数据库的初始密码,实际如果目录不一样记得换成自己的目录。下载后通过远程将安装包上传至服务器,我这里将安装包上传至/usr/local/mysql 下。ps:这时候如果还是不能远程访问的话,可能是防火墙的原因,命令行执行以下命令即可。再次登录,发现缺少 libtinfo.so.5文件,再次建立软连接。登录,发现报错,提示缺少libncurses.so.5文件,使用。创建 /var/lib/ 下mysql目录。登录成功,修改密码,设置远程访问。

为谁伫立在风中 1万+

ThreadLocal

这是因为threadlocal底层使用的数据结构是threadlocalmap,key是一个threadlocal对象的弱引用,当进行垃圾回收时,threadlocal对象被回收,于是key变成null,但是value还是有值的,因此这个value就无法通过key找到。ThreadLocal对象有两个方法set方法和get方法,所以每一个ThreadLocal对象都有set,get两个方法,用于给这个ThreadLocal对象设置值和取出值。1.通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。

weixin_47414034的博客 471

AutoSAR从0开始实战 --- 目录

​​​​​​本专栏是全网最真实的AutoSAR实战专栏,博主将从一个0基础小白的身份来讲解AutoSAR工具的配置,以及相应代码的讲解。

wj博客 1729

ThreadLocal详解包括Get、Set方法以及,ThreadLocal内存泄漏问题。

我们可以看到此处的set方法,set方法的入参为泛型对应我们上述创建的ThreadLocal时填入的泛型。我们可以看到该类是由泛型修饰的,所以我们在创建的时候可以通过泛型去控制ThreadLocal里面存放的数据类型。这里我定义的泛型为String,我们通过set方法往对应线程里面设置参数。所以它具体的初始化应该是在它的其他方法调用的时候进行初始化的。ThreadLocal的创建是通过New的方式生成的。为C实现的一个本地方法是为了获取当前的线程信息。首先获取到Enrty数组,获取数组的长度。

zzzzzzzzzzzzzzzy的博客 1335

4.Simulink基础建模操作——加减乘除运算

加减乘除运算

FPGA/MATLAB学习教程/源码/项目合作开发 9551

36、R语言中的回归模型与机器学习算法

本文介绍了R语言中多种回归模型和机器学习算法,包括Cox回归模型、数据平滑方法(如样条函数、多项式曲面拟合和核平滑)以及回归树模型等。重点讲解了这些方法的实现函数、参数说明以及实际应用示例,如使用`coxph`进行生存分析、使用`spline`和`smooth.spline`进行数据平滑处理、使用`loess`和`locpoly`进行非参数回归分析,以及使用`rpart`构建和可视化回归树。此外,还涉及了模型评估与优化策略,如数据拆分、交叉验证和树的修剪。通过这些方法,可以更好地处理复杂数据集并提升模型性能

yhn456789的博客 122

ThreadLocal的get方法、set方法,以及其内存泄露问题

既然多线程访问同一个变量会造成线程安全的问题,那么创建出来一个变量,需要使用这个变量的线程将该变量拷贝一份,并且拷贝到每一个线程的变量是线程私有的,使得变量在线程之间隔离起来使用,避免了线程之间的交错使用数据造成的线程安全问题。

小金的博客 995

把 SM30 用顺:从表设计到权限与传输,全面掌握 SAP Table Maintenance Generator

摘要 SAP的Table Maintenance Generator(TMG)是为配置表维护需求设计的经典工具,能够生成标准化的维护对话框(SM30)。它支持单表、维护视图(多表关联)和视图簇(导航式维护)三种维护对象类型。使用TMG前需注意三个关键设置:1)表维护权限选项;2)交付类(决定数据传输行为);3)客户端字段配置。通过SE54/SE11生成维护对话框时,需选择维护类型(一步/两步)和记录例程(控制传输)。TMG生成的对象包含函数组、屏幕和处理模块,但部分标准框架不可修改。实际应用中,可通过参数事

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 176

java面试:了解ThreadLocal吗?ThreadLocal的内存泄露问题发生的原因是什么?

ThreadLocal 是 Java 中一个特殊的类,它会为每个线程提供局部变量,这些变量与普通变量的区别在于,每个访问该变量的线程都有自己独立初始化的副本,互不干扰。通过设置独立变量这种方式来实现线程之间的数据隔离来保证互斥性。通过以上分析,我们得知了无论如何ThreadLocal都会造成内存泄漏的情况,key为强引用就会导致ThreadLocal的内存泄漏,而key为弱引用下会导致强引用的Value产生内存泄漏。

2301_80939853的博客 1157

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Java面试必问,深入理解 ThreadLocal 实现原理与内存泄露

前言 在面试环节中,考察"ThreadLocal"也是面试官的家常便饭,所以对它理解透彻,是非常有必要的. 有些面试官会开门见山的提问: “知道ThreadLocal吗?” “讲讲你对ThreadLocal的理解” 当然了,也有面试官会慢慢引导到这个话题上,比如提问“在多线程环境下,如何防止自己的变量被其它线程篡改”,将主动权交给你自己,剩下的靠自己发挥。 那么ThreadLocal可以...

weixin_46062001的博客 170

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

头条二面:你确定ThreadLocal真的会造成内存泄露?,今日头条java面试难吗

我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。面试题多多少少对于你接下来所要做的事肯定有点帮助,但我更希望你能透过面试题去总结自己的不足,以提高自己核心技术竞争力。每一次面试经历都是对你技术的扫盲,面试后的复盘总结效果是极好的!网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)

2401_83977530的博客 364

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Java面试题:细数ThreadLocal大坑,内存泄露本可避免

ThreadLocal是Java中用于解决多线程共享变量导致的线程安全问题的一种机制。它为每个线程分配一个独立的变量副本,从而避免了线程间的数据竞争。然而,如果使用不当,ThreadLocal也可能导致内存泄露。

515

【Java面试】这么回答提升30%的面试通过率,ThreadLocal出现内存泄露吗?

下次面试的时候遇到这个问题,大家知道怎么回答了吗?!!另外,我将所有Java面试系列制作成了完整的面试文档。它的便捷之处在于,可以通过检索的方式,找到你想要的面试题,目前已经更新200期,总计超过20W字!

跟着Mic学架构 488

Java研发岗面试必问,深入理解 ThreadLocal 实现原理与内存泄露

前言 在面试环节中,考察"ThreadLocal"也是面试官的家常便饭,所以对它理解透彻,是非常有必要的. 有些面试官会开门见山的提问: “知道ThreadLocal吗?” “讲讲你对ThreadLocal的理解” 当然了,也有面试官会慢慢引导到这个话题上,比如提问“在多线程环境下,如何防止自己的变量被其它线程篡改”,将主动权交给你自己,剩下的靠自己发挥。 那么ThreadLocal可以做什么,在了解它的应用场景之前,我们先看看它的实现原理,只有知道了实现原理,才好判断它是否符合自己的...

m0_49496327的博客 100

Java最全头条二面:你确定ThreadLocal真的会造成内存泄露?(3),Java面试基础知识

机会是留给有准备的人,大家在求职之前应该要明确自己的态度,熟悉求职流程,做好充分的准备,把一些可预见的事情做好。对于应届毕业生来说,校招更适合你们,因为绝大部分都不会有工作经验,企业也不会有工作经验的需求。同时,你也不需要伪造高大上的实战经验,以此让自己的简历能够脱颖而出,反倒会让面试官有所怀疑。你在大学时期应该明确自己的发展方向,如果你在大一就确定你以后想成为Java工程师,那就不要花太多的时间去学习其他的技术语言,高数之类的,不如好好想着如何夯实Java基础。请转发本文支持一下本文已被。

2401_84691837的博客 854

Java最新头条二面:你确定ThreadLocal真的会造成内存泄露?,java开发岗银行面试题

文章中涉及到的知识点我都已经整理成了资料,录制了视频供大家下载学习,诚意满满,希望可以帮助在这个行业发展的朋友,在论坛博客等地方少花些时间找资料,把有限的时间,真正花在学习上,所以我把这些资料,分享出来。相信对于已经工作和遇到技术瓶颈的朋友们,在这份资料中一定都有你需要的内容。本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录需要这份系统化的资料的朋友,可以点击这里获取。

2401_84692253的博客 845

如何用Java编写一段代码引发内存泄露

本文来自StackOverflow问答网站的一个热门讨论:如何用Java编写一段会发生内存泄露的代码。   Q:刚才我参加了面试,面试官问我如何写出会发生内存泄露的Java代码。这个问题我一点思路都没有,好囧。 A1:通过以下步骤可以很容易产生内存泄露(程序代码不能访问到某些对象,但是它们仍然保存在内存中)           1、应用程序创建一个长时间运行的线程(或者使用线程池,会更...

小程故事多 540

ThreadLocal 深度解析:Hash 冲突解决、内存泄露及设计理念

本场面试通过四轮提问,涵盖了ThreadLocal的多个关键点。马架构凭借其十年的Java项目研发经验和架构设计经验,逐一解答了面试官提出的问题,并展现了深厚的理论知识和丰富的实战经验。

南客先生的博客 368
上一篇: Java设计模式系列(一):单例模式&原型模式
下一篇: Java面试之HashMap的红黑树
Machine4869
博客等级 码龄10年 9粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值