ThreadLocal 源码详解

概述

ThreadLocal是一个java提供的本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不通的变量值完成操作的场景

方法

作用

public T get()

获取当前线程的副本变量值

public void set(T value)

保存当前线程的副本变量值

public void remove()

移除当前线程的副本变量值

protected T initialValue()

为当前线程初始副本变量值

ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象

底层结构

ThreadLocal是一个工具类,用来管理每个线程中的ThreadLocalMap

ThreadLocalMap就是「存储线程中产生的ThreadLocal变量以及当前线程的变量副本值的一个map表」,ThreadLocalMap是ThreadLocal中的静态内部类,并没有实现map接口,用独立的方式实现了map的功能,其内部的entry也独立实现

  • 每个Thread线程内部都有一个Map,即ThreadlLocalMap
  • Map里面存储线程本地对象(key)和线程的变量副本(value)
  • 但是,Thread内部的Map-ThreadLocalMap是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值。所以对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰

源码解析

get()

public T get() {Thread t = Thread.currentThread();//获得当前线程ThreadLocalMap map = getMap(t);//ThreadLocalMap是ThreadLocal的一个静态类,if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);//传入的是this,this代表当前对象,即当前这个threadLocalif (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();//类的私有函数,初始化数据,创建map表}ThreadLocalMap getMap(Thread t) {return t.threadLocals;//threadLocals是Thread类中的成员变量
}private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);//创建map表并设置数据return value;
}protected T initialValue() {return null;
}

流程:

    1. 获取当前线程t
    2. 根据t调用getMap()函数获取当前线程的成员变量ThreadLocalMap
    3. 判断map是否为空
      1. 不为空从map中获取线程存储的k-v entry结点,再从entry结点中获取存储的value副本并返回
      2. 为空调用setInitialValue(),创建map表并设置数据(即数据为null)

set()

public void set(T value) {Thread t = Thread.currentThread();//获取当前线程ThreadLocalMap map = getMap(t);//获取线程中的threadLocalMapif (map != null)map.set(this, value);elsecreateMap(t, value);
}ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}

流程

    1. 获取当前线程t
    2. 根据t调用getMap()函数获取当前线程的成员变量ThreadLocalMap
      1. map非空,则将threadLocal和新的value副本放入map中
      2. map空,则对线程的成员变量ThreadLocalMap进行初始化创建,并将threadLocal和value副本放入map中

remove()

public void remove() {//获取线程中的成员变量threadLocalMapThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);
}ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}

流程

也是根据当前线程获取 成员变量thredLocalMap,再移除当前对象

内存泄漏

static class Entry extends WeakReference<ThreadLocal> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal k, Object v) {super(k);//k是弱引用value = v;}
}

Entry中的key是弱引用的,每个key都弱引用执行threadLocal,当threadLocal实例置为null以后,没有任何强引用指向threadLocal实例,所以threadLocal将会被GC回收,但是我们的value却不能回收,而这块value也不会访问到了,只有等到线程结束时value才能会回收,但是如果使用的是线程池,线程干完任务之后就会被放入池中,线程永远不会结束,就会造成内存泄漏。

在ThreadLocal中的set(),get(),remove()方法在每次操作ThreadLocalMap中的值是都会清理ThreadLocalMap中key为null的value

所以,当某个ThreadLocal变量不再使用时,记得调用remove()清理该变量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/326153.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

美国政府首次发布《国家网络安全态势报告》

报告提到&#xff0c;不断演变的关键基础设施风险、勒索软件、供应链利用、商业间谍软件和AI是主要趋势&#xff1b;国家网络总监办公室同时公布了第二版《国家网络安全战略实施计划》&#xff0c;新增了31项倡议。 前情回顾美国深化网络安全战略 美国发布国家网络安全战略实施…

快团团怎么做帮卖团长/供货大团长(如何从小白到优质团长)?

一名小白想要成长为快团团的优质团长&#xff0c;可以遵循以下步骤和策略&#xff1a; 了解平台与注册成为团长&#xff1a; 首先&#xff0c;熟悉快团团平台的操作流程和规则。快团团是一个基于微信的小程序&#xff0c;专注于社区团购业务。通过微信扫描团长资源二维码或在快…

【爬虫基础1.1课】——requests模块上

目录索引 requests模块的作用&#xff1a;实例引入&#xff1a; 特殊情况&#xff1a;锦囊1&#xff1a;锦囊2: 这一个栏目&#xff0c;我会给出我从零开始学习爬虫的全过程。感兴趣的小伙伴可以关注一波&#xff0c;用于复习和新学都是不错的选择。 那么废话不多说&#xff0c…

​​​​【收录 Hello 算法】5.2 队列

目录 5.2 队列 5.2.1 队列常用操作 5.2.2 队列实现 1. 基于链表的实现 2. 基于数组的实现 5.2.3 队列典型应用 5.2 队列 队列&#xff08;queue&#xff09;是一种遵循先入先出规则的线性数据结构。顾名思义&#xff0c;队列模拟了排队现象&#xff0c;即…

利用香港多IP服务器进行大数据分析的潜在优势?

利用香港多IP服务器进行大数据分析的潜在优势? 在当今数据驱动的时代&#xff0c;大数据分析已经成为企业获取竞争优势的不二选择。而香港作为一个拥有世界级通信基础设施的城市&#xff0c;提供了理想的环境来部署多IP服务器&#xff0c;从而为大数据分析提供了独特的优势。…

2024中国(重庆)人工智能展览会8月举办

2024中国(重庆)人工智能展览会8月举办 邀请函 主办单位&#xff1a; 中国航空学会 重庆市南岸区人民政府 招商执行单位&#xff1a; 重庆港华展览有限公司 【报名I59交易会 2351交易会 9466】 展会背景&#xff1a; 2024中国航空科普大会暨第八届全国青少年无人机大赛在…

Spring Framework-简介

Spring Framework Java Spring是一个开源的Java应用框架&#xff0c;它的主要目的是简化企业级应用开发的复杂性。Spring框架为开发者提供了许多基础功能&#xff0c;使得开发者能够更专注于业务逻辑的实现&#xff0c;而不是底层的细节。 主要特点和功能&#xff1a; 控制反…

数据库脚本编写规范(SQL编写规范)

编写本文档的目的是保证在开发过程中产出高效、格式统一、易阅读、易维护的SQL代码。 1 编写目 2 SQL书写规范 3 SQL编写原则 软件开发全文档获取&#xff1a;点我获取

全域运营是割韭菜吗?看完再下结论!

随着流量时代的到来&#xff0c;各大公私域平台中的流量争夺战日益激烈&#xff0c;商家和品牌实现流量变现的难度值也不断提高&#xff0c;运营人员的压力也逐渐增大。在此背景下&#xff0c;全域运营的兴起或许是一个契机&#xff0c;能够将所有人从内卷的状态中解救出来。而…

网络安全之OSPF进阶

该文针对OSPF进行一个全面的认识。建议了解OSPF的基础后进行本文的一个阅读能较好理解本文。 OSPF基础的内容请查看&#xff1a;网络安全之动态路由OSPF基础-CSDN博客 OSPF中更新方式中的触发更新30分钟的链路状态刷新。是因为其算法决定的&#xff0c;距离矢量型协议是边算边…

力扣刷题 day1

移动零 283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 看到这道题&#xff0c;是否第一个想法就是双指针&#xff0c;下面我们就来使用双指针来完成这道题. 图: 代码: java: // 移动 0 双指针public void moveZeroes(int[] nums) {for (int current 0, dest -1; …

前端框架 Vue 主要用来做什么的?

Vue.js 是一个流行的前端框架&#xff0c;主要用于构建交互式的用户界面。它的设计目标是通过简单的 API 提供高效的数据驱动视图层。Vue 具有响应式数据绑定和组件化的特性&#xff0c;使得开发者可以轻松地构建复杂的单页面应用 (SPA) 和动态网页。 1. 数据驱动视图 Vue 的…

Matlab如何导出高质量论文插图?科研效率UpUp第8期

当你用Matlab绘制了一张论文插图&#xff1a; 想要所见即所得&#xff0c;原封不动地将其保存下来&#xff0c;该怎么操作呢&#xff1f; 虽说以前总结过7种方法&#xff08;Matlab导出论文插图的7种方法&#xff09;&#xff0c;但要说哪一种可以满足上面的要求&#xff0c;想…

socket实现TCP UDP

1、socket通信建立流程 1.1、创建服务端流程 使用 socket 函数来创建 socket服务。 使用 bind 函数绑定端口。 使用 listen 函数监听端口。 使用 accept 函数接收客户端请求。 1.2、创建客户端流程 使用 socket 函数来创建 socket 服务。 使用 connect 函数连接到 socke…

TypeError: can only concatenate str (not “int“) to str

TypeError: can only concatenate str (not "int") to str a 窗前明月光&#xff0c;疑是地上霜。举头望明月&#xff0c;低头思故乡。 print(str_len len(str_text) : len(a)) 试图打印出字符串 a 的长度&#xff0c;但是在 Python 中拼接字符串和整数需要使用字符…

邮件代发邮箱API发送邮件时如何正确使用?

邮件代发API发送邮件如何使用&#xff1f;邮件代发的注意事项&#xff1f; 邮件代发邮箱API作为邮件发送的自动化工具&#xff0c;其正确使用对于提高工作效率、保障信息安全具有重要意义。下面&#xff0c;AokSend就来探讨一下在使用邮件代发邮箱API发送邮件时&#xff0c;应…

【Linux学习笔记】一篇文章彻底搞定 “Linux同步与互斥“ !

本章重点 1. 学会线程同步。 2 学会使用互斥量&#xff0c;条件变量&#xff0c;posix信号量&#xff0c;以及读写锁。 1、进程线程间的互斥相关背景概念 临界资源&#xff1a;多线程执行流共享的资源就叫做临界资源临界区&#xff1a;每个线程内部&#xff0c;访问临界资源的…

Springboot项目使用redis实现session共享

1.安装redis&#xff0c;并配置密码 这里就不针对于redis的安装约配置进行说明了&#xff0c;直接在项目中使用。 redis在windows环境下安装&#xff1a;Window下Redis的安装和部署详细图文教程&#xff08;Redis的安装和可视化工具的使用&#xff09;_redis安装-CSDN博客 2…

C++青少年简明教程:C++程序结构

C青少年简明教程&#xff1a;C程序结构 一个简单的C程序源码如下&#xff1a; #include <iostream> using namespace std;int main() {cout << "Hello World" << endl;return 0; }下面解析一下。 1. #include <iostream> 这是一条预处理…

Request请求数据 (** kwargs参数)

目录 &#x1f31f;前言&#x1f349;request入门1. params2. data3. json4. headers5. cookies6. auth7. files8. timeout9. proxies10. allow_redirects11. stream12. verify13. cert &#x1f31f;总结 &#x1f31f;前言 在Python中&#xff0c;发送网络请求是一项常见的任…