首页 > 其他分享 >Thread介绍与原理分析

Thread介绍与原理分析

时间:2023-12-06 21:32:12浏览次数:32  
标签:Thread value ThreadLocal 线程 介绍 原理 TLM 引用

TheadLocal介绍及用法

ThreadLocal是线程的本地变量。当使用ThreadLocal维护变量时,它为每个线程提供独立的变量副本。

每一个线程可以独立地操作自己的变量,不受其他线程影响。

使用场景

  • 作为数据副本,当某些数据是以线程为作用域并且不同线程有不同数据副本,使用ThreadLocal。
  • 保存线程上下文信息,在任意需要的地方可以获取,避免显示传参。
  • 解决线程安全问题,避免某些情况需要考虑线程安全必须同步带来的性能损失。

ThreadLocal与Synchronized

ThreadLocal是与线程绑定的一个变量,其与Synchronized都用于解决多线程并发问题。

但Synchronized设计用于线程间变量共享;而ThreadLocal用于线程间变量隔离

Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象

Thread实现原理与内部结构

ThreadLocal

ThreadLocal由两个部分组成,ThreadLocal与ThreadLocalMap,后者为前者的一个静态内部类。

ThreadLocal的核心方法有三个:set(), **get()remove()**。

ThreadLocalMap

ThreadLocalMap(简称TLM),TLM是ThreadLocal的核心。本质上,TLM是一个定制化的HashMap。

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

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

它继承了弱引用(WeakReference),键是ThreadLocal本体,值就是我们存入的value。在默认情况下, 每个线程中的这两个变量都为null。

在创建Map时,传入的Thread的内部成员变量会引用该TLM。

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

方法解析

set()

public void set(T value) {
    	// 1.获取调用线程(当前线程)
        Thread t = Thread.currentThread();
    	// 2.寻找当前线程的TLM
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            // 3.1如果TLM存在,则直接set
            map.set(this, value);
        } else {
            // 3.2如果TLM不存在,就新建TLM
            createMap(t, value);
        }
    }

getMap()方法:

返回当前线程引用的TLM。

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

get()

public T get() {
        // 1.获取当前线程
        Thread t = Thread.currentThread();
        // 2.获取TLM
        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;
            }
        }
    	//如果是数据为null,则初始化,初始化的结果,TheralLocalMap中存放key值为threadLocal,值为null
        return setInitialValue();
    }

setInitialValue():

private T setInitialValue() {
    	// 1.创建默认值,实际上就是null
        T value = initialValue();
    	// 2-3.获取线程和TLM
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
    	// 4.根据map不同情况执行不同操作
        if (map != null) {
            map.set(this, value);
        } else {
            createMap(t, value);
        }
    	// JDK11新增了TerminatingThreadLocal类,主要用于解决内存泄露问题。
        if (this instanceof TerminatingThreadLocal) {
            TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
        }
        return value;
    }

remove():

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null) {
        m.remove(this);
    }
}

本质

由以上分析我们可以知道,本地变量实际存放在调用线程的threadLocals,ThreadLocal只是一个封装壳。

ThreadLocal的核心是TLM。

ThreadLocal内存泄露问题

名词解释:

内存溢出(Out of Memory)

内存溢出是指程序在申请内存时,没有足够的内存供其使用。

内存泄露(MemoryLeak)

内存泄露是指程序在申请内存后,无法或者未释放已经申请到的内存。

MemoryLeak积少成多,会导致Out of Memory。

强引用与弱引用

TLM的Entry显式继承了WeakRereference的ThreadLocal。

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

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

引用类型一般在java.lang.ref包下,常见的引用有:FinalReference, SoftReference以及WeakReference。

强引用(StrongReference)

StrongReference在上述包下并没有实际的对应类,但我们程序中几乎所有的引用使用的都是强引用。

例如

StringBuilder sb = new StringBuilder(); 
// 这里栈上的sb对堆上的StringBuilder对象是强引用

强引用可以直接访问目标对象,并且指向的对象任何时候都不会被回收,即使不回收该对象会抛出OOM(OutOfMemory),JVM也不会回收。

弱引用(WeakReference)

弱引用中的对象具有很短的声明周期,因为在系统GC时,只要发现弱引用,不管堆空间是否足够,都会将对象进行回收。由于垃圾回收器是一个优先级很低的线程,因此不一定立即发现那些只具有弱引用的对象。

问题原因

ThreadLocal是WeakReference,如果一个ThreadLocal没有外部强引用来引用它,就会被GC回收。

ThreadLocalMap中就会出现key为null的Entry,key为null,则无法访问这些Entry的value。

而在线程池技术下,线程经常服用,生命周期非常长,甚至与JVM共生。

这样一条引用链会一直保持:Thread Ref-> Thread -> ThreaLocalMap -> Entry -> value

这会导致value无法被回收,引起内存泄露。

ThreadLocal为什么要弱引用

如果使用强引用的话

ThreadLocalMap的生命周期基本和Thread的生命周期一样,当前线程如果没有终止,那么ThreadLocalMap始终不会被GC。ThreadLocalMap持有对ThreadLocal的强引用,那么ThreadLocal也不会被GC,当线程生命周期长,如果没有手动删除,则会造成Entry的累积,从而导致OOM。

从另一个方面来说,强引用只是在GC层面将内存泄露的问题掩盖起来,并没有真正解决问题

由上可知:引起内存泄露的根本原因不是弱引用,而是因为TLM与Thread共生,即TLM生命周期和Thread保持一致。

所以,在使用时一定要使用完ThreadLocal后调用remove()方法清理。

标签:Thread,value,ThreadLocal,线程,介绍,原理,TLM,引用
From: https://blog.51cto.com/ErickRen/8710750

相关文章

  • forms组件介绍
    1注册功能,登录功能,前端需要校验(字段长度,邮箱是否合法。。。)2前端校验可以没有,后端校验是必须的,使用传统方式if判断写的很多3借助于forms组件,可以快速实现字段的校验 fromdjango.formsimportForm 注意:#form=myforms.MyForm(data)实例化得到对象#校验数据:form.is_v......
  • 基础介绍
    基本概念简介Kafka是由Apache开发的一个开源流处理平台,由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。对于像Hadoop一样的日志数据和离线分析系......
  • TF-IDF原理及Sklearn实现
    TF-IDF算法介绍TF-IDF(termfrequency–inversedocumentfrequency,词频-逆向文件频率)是一种用于信息检索(informationretrieval)与文本挖掘(textmining)的常用加权技术。TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随......
  • 转置原理与多项式多点求值
    终于学转置原理了,之前一直听zhy糊多项式题不知道他在讲写啥。自己的多项式水平长期停留在多项式除法,直到今天做互测时被迫学了怎么去多点求值。正式比赛大概率不考(吧?)所以学来娱乐一下。普通多点求值算法思想很妙,效率很逊。代码不写了因为我连多项式取模都忘了怎么写了。考虑......
  • xss专题1-原理解析和简单利用
    XSS原理解析跨站脚本攻击(XSS)是一种常见的网络安全漏洞,其原理涉及恶意用户向网页注入客户端脚本代码,使其在用户的浏览器中执行。攻击者利用输入栏或其他用户可输入内容的地方,注入包含恶意脚本的数据。当其他用户访问包含恶意注入内容的页面时,这些脚本将在其浏览器中执行,导致攻击者......
  • 09-序列化器的 many=True 实现原理
    入门知识上面说明了,解释器会先执行new方法,再执行init方法下面说明了,如果new返回的空对象不是当前init对应的类型,就不会执行init。序列化器many=True的简化版可以看到many_init方法返回的是Alist的空对象,而不是A的空对象,因此,没有Ainit打印1.BookSerializer(da......
  • C2 CompilerThread9 长时间占用CPU解决方案
    一、问题描述近期在进行日常巡检时发现,线上部分应用服务器的CPU突然比以往高出很多,经过登录机器排查确认是C2CompilerThread9线程始终长时间运行消耗了CPU。排查步骤在上篇博文有记录总结,地址:排查CPU异常步骤_u012538947的专栏-CSDN博客_cpu异常异常线程的堆栈如下:"C2Compile......
  • 离散元分析有什么用?仿真软件介绍
    离散元分析是一种用于模拟颗粒材料行为的数值方法,它将颗粒材料看作由大量离散的颗粒或颗粒群组成的系统,通过模拟颗粒之间的相互作用来研究颗粒材料的力学行为、变形和破坏等现象。离散元分析在工程领域有着广泛的应用,为工程师提供了一个强大的工具,用于研究颗粒材料的行为和性能,为......
  • 【Java 进阶篇】Java Session 原理及快速入门
    大家好,欢迎来到本篇博客。今天,我们将探讨JavaWeb开发中一个重要而令人兴奋的概念,即Session(会话)。Session是一种在Web应用程序中跟踪用户状态和数据的机制。我们将深入了解Session的原理,并通过示例来快速入门。什么是Session?在Web开发中,Session是一种服务器端的机制,用于跟踪用户与W......
  • 【backward解决方案与原理】网络模型在梯度更新时出现变量版本号机制错误
    【backward解决方案与原理】网络模型在梯度更新时出现变量版本号机制错误报错详情错误产生背景原理解决方案RuntimeError:oneofthevariablesneededforgradientcomputationhasbeenmodifiedbyaninplaceoperation报错详情  模型在backward时,发现如下报错......