首页 > 其他分享 >对ThreadLocal的理解

对ThreadLocal的理解

时间:2023-12-17 21:33:00浏览次数:28  
标签:value ThreadLocal 理解 线程 内存 key 引用

1.ThreadLocal概述

ThreadLocal是多线程中对于解决线程安全的一个操作类,它会为每个线程都分配一个独立的线程副本从而解决了变量并发访问冲突的问题。ThreadLocal 同时实现了线程内的资源共享

案例:使用JDBC操作数据库时,会将每一个线程的Connection放入各自的ThreadLocal中,从而保证每个线程都在各自的 Connection 上进行数据库的操作,避免A线程关闭了B线程的连接。

对ThreadLocal的理解_ThreadLocal

2. ThreadLocal的实现原理&源码解析

ThreadLocal本质来说就是一个线程内部存储类,从而让多个线程只操作自己内部的值,从而实现线程数据隔离

每个线程内有一个 ThreadLocalMap 类型的成员变量,用来存储资源对象

对ThreadLocal的理解_ThreadLocal_02

ThreadLocalMap 的一些特点

  • key 的 hash 值统一分配
  • 初始容量 16,扩容因子 2/3,扩容容量翻倍
  • key 索引冲突后用开放寻址法解决冲突

2.1. ThreadLocal基本使用

  • set(value) 设置值:  以 ThreadLocal 自己作为 key,资源对象作为 value,放入当前线程的 ThreadLocalMap 集合中
  • get() 获取值: 以 ThreadLocal 自己作为 key,到当前线程中查找关联的资源值
  • remove() 清除值: 以 ThreadLocal 自己作为 key,移除当前线程关联的资源值

代码案例

public class ThreadLocalTest {
    static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        new Thread(() -> {
            String name = Thread.currentThread().getName();
            threadLocal.set("zbbmeta");
            print(name);
            System.out.println(name + "-after remove : " + threadLocal.get());
        }, "t1").start();
        new Thread(() -> {
            String name = Thread.currentThread().getName();
            threadLocal.set("zbbmeta");
            print(name);
            System.out.println(name + "-after remove : " + threadLocal.get());
        }, "t2").start();
    }

    static void print(String str) {
        //打印当前线程中本地内存中本地变量的值
        System.out.println(str + " :" + threadLocal.get());
        //清除本地内存中的本地变量
        threadLocal.remove();
    }

}

3. ThreadLocal-内存泄露问题

在介绍内存泄露问题问题之前先介绍一下Java对象中的四种引用类型:Java对象中的四种引用类型:

  • 强引用: 最为普通的引用方式,表示一个对象处于有用且必须的状态,如果一个对象具有强引用,则GC并不会回收它。即便堆中内存不足了,宁可出现OOM,也不会对其进行回收
Object obj = new Object();
  • 软引用:软引用用于描述一些还有用但并非必须保持的对象。在系统即将发生内存溢出之前,垃圾收集器会清理这些软引用指向的对象
Object obj = new Object();
SoftReference<Object> softRef = new SoftReference<>(obj);
  • 弱引用:表示一个对象处于可能有用且非必须的状态。在GC线程扫描内存区域时,一旦发现弱引用,就会回收到弱引用相关联的对象。对于弱引用的回收,无关内存区域是否足够,一旦发现则会被回收
Object obj = new Object();
WeakReference<Object> weakRef = new WeakReference<>(obj);
  • 虚引用:虚引用也称为幽灵引用,它几乎没有实际意义,主要用于跟踪对象被垃圾收集的活动;虚引用不能单独使用,必须与引用队列(ReferenceQueue)一起使用。当垃圾收集器准备回收一个对象时,如果发现它有虚引用,会把这个虚引用加入到与之关联的引用队列中
Object obj = new Object();
PhantomReference<Object> phantomRef = new PhantomReference<>(obj, referenceQueue);

3.1. ThreadLocal-内存泄露问题

每一个Thread维护一个ThreadLocalMap,在ThreadLocalMap中的Entry对象继承了WeakReference,其中key为使用弱引用的ThreadLocal实例,value为线程变量的副本

对ThreadLocal的理解_ThreadLocal_03

ThreadLocalMap 中的 key 被设计为弱引用,原因如下

Thread 可能需要长时间运行(如线程池中的线程),如果 key 不再使用,需要在内存不足(GC)时释放其占用的内存

内存释放时机

  • 被动 GC 释放 key
  • 仅是让 key 的内存释放,关联 value 的内存并不会释放
  • 懒惰被动释放 value
  • get key 时,发现是 null key,则释放其 value 内存

  • set key 时,会使用启发式扫描,清除临近的 null key 的 value 内存,启发次数与元素个数,是否发现 null key 有关

  • 主动 remove 释放 key,value

  • 会同时释放 key,value 的内存,也会清除临近的 null key 的 value 内存

  • 推荐使用它,因为一般使用 ThreadLocal 时都把它作为静态变量(即强引用),因此无法被动依靠 GC 回收

4. ThreadLocal提问

对ThreadLocal的理解?

ThreadLocal 主要功能有两个:

  • 第一个是可以实现资源对象的线程隔离,让每个线程各用各的资源对象,避免争用引发的线程安全问题
  • 第二个是实现了线程内的资源共享

ThreadLocal的底层原理实现?

在ThreadLocal内部维护了一个一个 ThreadLocalMap 类型的成员变量,用来存储资源对象

当我们调用 set 方法,就是以 ThreadLocal 自己作为 key,资源对象作为 value,放入当前线程的 ThreadLocalMap 集合中

当调用 get 方法,就是以 ThreadLocal 自己作为 key,到当前线程中查找关联的资源值

当调用 remove 方法,就是以 ThreadLocal 自己作为 key,移除当前线程关联的资源值

关于ThreadLocal会导致内存溢出这个事情的解决方案?

是应为ThreadLocalMap 中的 key 被设计为弱引用,它是被动的被GC调用释放key,不过关键的是只有key可以得到内存释放,而value不会,因为value是一个强引用。

在使用ThreadLocal 时都把它作为静态变量(即强引用),因此无法被动依靠 GC 回收,建议主动的remove 释放 key,这样就能避免内存溢出。

标签:value,ThreadLocal,理解,线程,内存,key,引用
From: https://blog.51cto.com/maguobin/8863474

相关文章

  • 理解K8S的CRD
    CRD本身是Kubernetes内置的资源类型,全称是CustomResourceDefinition,可以通过命令查看,kubectlget查看集群内定义的CRD资源。NAMECREATEDATapps.app.o0w0o.cn2019-07-25T07:02:47Zmicroservices.app.o0w0o.cn2019-07-25T07:02:47Z在与人......
  • 语文阅读理解总结
    需要注意的事项:1.答题思路要保持清晰,如果题目让分析词语的作用,首先应该写出这个词语的意思,再根据其释义分析作用例如:问:第六段加点词“越来越多”体现了说明文的准确性,请具体说明原始答案:越来越多说明人们更加地重视保护叫卖声,不希望叫卖声就此消失参考答案:越来越多表示数量逐......
  • 理解 Paimon changelog producer
    介绍目的Chaneglogproducer的主要目的是为了在Paimon表上产生流读的changelog,所以如果只是批读的表是可以不用设置Chaneglogproducer的.一般对于数据库如MySQL来说,当执行的语句涉及数据的修改例如插入、更新、删除时,MySQL会将这些数据变动记录在binlog中。相......
  • 我对过拟合的理解
    1、什么是过拟合就是训练出来的模型在训练集上表现很好,但是在测试集上表现较差的一种现象,如下图:2、发生过拟合的主要原因(1)数据有噪声(2)训练数据不足,有限的训练数据(3)训练模型过度导致模型非常复杂3、如何防止(1)获取和使用更多的数据(数据集增强)(2)采用合适的模(3)降低特征的数(4)Dropout是在......
  • ThreadLocal
    publicclassUserContext{privatestaticThreadLocal<User>userThreadLocal=newThreadLocal<>();publicstaticvoidsetUser(Useruser){userThreadLocal.set(user);}publicstaticUsergetUser(){......
  • wifi 的频段 与网络协议的理解。
    什么是网络协议        什么是WiFI的频段         关于rtl8723DU的网络协议以及频段。  ......
  • 理解数字化转型:3个阶段、2个分类和3类价值
    导读:数字化转型是基于IT技术提供业务所需要的支持,让业务和技术真正产生交互而诞生的。我们可以从概念及内涵、分类、价值等多个维度来理解企业数字化转型。  01数字化转型的概念及内涵数字化转型运用5G、人工智能、大数据、云计算等新一代数字技术,改变企业为客户创造价值的业务......
  • 【2023-12-15】理解包容
    11:00负担越重,我们的生命越贴近大地,它就越真切实在。                                                 ——米兰·昆德拉今天下午奶奶要回老家,得提前下班去接手二宝。......
  • Linux tracepoint使用和理解
    参考:如何使用TRACE_EVENT()宏来创建跟踪点1.如何查看tracepoint/sys/kernel/debug/tracing/events/通过perflisttracepoint查看2.tracepoint理解因此tracepoint方式如下:#undefTRACE_SYSTEM#defineTRACE_SYSTEMxhr_test#if!defined(_TRACE_TE_TEST_H)||def......
  • 如何理解十二要素方法论
    本文翻译自HowToUnderstandTwelve-FactorMethodologyInSpringBootApplications,原作者:OLEKSII。十二要素应用程序是构建现代可扩展应用程序架构所需的一系列原则。本篇文章将从SpringBoot应用的角度回顾这些原则。一、代码库应用程序在版本控制系统(如Git)中进......