通过查看ThreadLocal的源码进行简单理解

目录

为什么要使用ThreadLocal?

简单案例

ThreadLocal源码分析 

断点跟踪


为什么要使用ThreadLocal

在多线程下,如果同时修改公共变量可能会存在线程安全问题,JDK虽然提供了同步锁与Lock等方法给公共访问资源加锁,但在高并发的场景下,如果多个线程争抢一把锁会出现大量的锁等待时间,让系统的响应时间变慢。因此JDK又提供了新的思路,用空间换取时间也就是使用ThreadLocal。

简单案例

先进行一个简单案例

@SpringBootTest
public class TestThreadLocal {private ThreadLocal<String> local = new ThreadLocal<>();@Testpublic void test01() throws Exception {new Thread(()->{local.set("local_A");System.out.println(Thread.currentThread().getName());System.out.println(local.get());},"a").start();new Thread(()->{local.set("local_B");System.out.println(Thread.currentThread().getName());System.out.println(local.get());},"b").start();}
}

运行结果为如下

a
local_A
b
local_B

待会我们断点查看上述代码的运行过程,在此之前我们先来查看一下ThreadLocal源码。

ThreadLocal源码分析 

首先给出ThreadLocal类中主要的代码如下

public class ThreadLocal<T> {public T get() {//获取当前线程Thread t = Thread.currentThread();//获取当前线程的成员变量ThreadLocalMap对象ThreadLocalMap map = getMap(t);if (map != null) {//根据threadLocal对象从map中获取Entry对象ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")//获取保存的数据T result = (T)e.value;return result;}}//如果没有进行set方法,那么执行该方法//初始化数据return setInitialValue();}private T setInitialValue() {//获取要初始化的数据,该方法需要自己重写实现T value = initialValue();//获取当前线程Thread t = Thread.currentThread();//获取当前线程的成员变量ThreadLocalMap对象ThreadLocalMap map = getMap(t);//如果map不为空if (map != null)//将初始值设置到map中,key是this,即threadLocal对象,value是初始值map.set(this, value);else//如果map为空,则需要创建新的map对象createMap(t, value);return value;}public void set(T value) {//获取当前线程Thread t = Thread.currentThread();//获取当前线程的成员变量ThreadLocalMap对象ThreadLocalMap map = getMap(t);//如果map不为空if (map != null)//将值设置到map中,key是this,即threadLocal对象,value是传入的value值map.set(this, value);else//如果map为空,则需要创建新的map对象createMap(t, value);}static class ThreadLocalMap {...}...
}

上面三个主要方法主要操作的是ThreadLocalMap对象,而ThreadLocalMap又是ThreadLocal的静态内部类。

主要的ThreadLocalMap的源码如下

static class ThreadLocalMap {//维护了一个静态内部类,用来存储主要的数据static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}//存在一个Entry数组,该table对象中主要存放多个ThreadLocal与value的值private Entry[] table;...
}

ThreadLocalMap中又维护了一个内部类Entry,同时Entry类又继承了WeakReference(弱引用与ThreadLocal对象)。

在Thread类中维护了一个ThreadLocalMap类型的变量threadLocals。

因此ThreadLocal的整体结构大致如下

每一个set()的数据都会被保存在Entry数组中。源码查看完毕,接下来我们断点跟踪上述案例。

断点跟踪

对set()方法中进行断点查看,当线程a第一次执行set()时,先获取ThreadLocalMap对象是否为空,如果为空,那么执行createMap()方法,将当前线程与需要存储的值作为参数传递

在createMap()中,只做了一件事,那就是创建一个对象并放入当前线程。

set()方法查看完毕,接下来查看get()方法做了什么事情

 在get方法中,获取当前线程以及线程中的map对象,从对象中根据key获取存储的值并返回。如果没有执行过set方法,会执行setInitalValue()方法。

而在该方法中其实也只做了一件事,那就是初始化值。

    private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value;}

initialValue()默认直接返回null,需要我们自己重写实现,具体代码如下

@SpringBootTest
public class TestThreadLocal {private ThreadLocal<User> local = new ThreadLocal<User>(){@Overrideprotected User initialValue() {User user = new User("张三", 18);return user;}};@Testpublic void test03() throws Exception {new Thread(()->{User user = local.get();System.out.println(user.toString());}).start();}
}

运行结果如下

User(name=张三, age=18)

 以上就是我对ThreadLocal的简单理解!

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

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

相关文章

力扣 --- H指数

题目描述&#xff1a; 给你一个整数数组 citations &#xff0c;其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。 根据维基百科上 h 指数的定义&#xff1a;h 代表“高引用次数” &#xff0c;一名科研人员的 h 指数 是指他&#xff…

【HDFS】调试慢节点pipiline ack信息

Client - DN1 - DN2 - DN3 DN3 send ack:[0][d3]。 DN2 send ack: [从dn2入队到收到dn3的ack耗时,0] [d2,d3]。 DN1 send ack: [pkt从dn1入队到收到dn2的ack耗时,pkt从dn2入队到收到dn3的ack耗时,0] [d1,d2,d3]。 Client receive: 就是DN1发送过来数据。 客户端收到的第一个…

【论文笔记】Universal Guidance for Diffusion Models

Abstract 典型的扩散模型经过训练以接受特定形式的条件作用&#xff08;最常见的是文本&#xff09;&#xff0c;并且如果不经过重新训练就不能接受其他形式的条件的作用。 这项工作中提出了一种通用制导算法(universal guidance algorithm)&#xff0c;使扩散模型能够通过任意…

python弹球小游戏

import pygame import random# 游戏窗口大小 WIDTH 800 HEIGHT 600# 定义颜色 WHITE (255, 255, 255) BLACK (0, 0, 0) RED (255, 0, 0) GREEN (0, 255, 0) BLUE (0, 0, 255)# 球的类 class Ball:def __init__(self):self.radius 10self.speed [random.randint(2, 4),…

DELL EMC unity 存储系统日志收集方法

对于一些非简单的硬件故障&#xff0c;解决故障最有效、最快速的方法就是收集日志&#xff0c;而不是瞎搞。常见的乱搞方法就是 1. reimage系统‘ 2. 更换控制器&#xff1b;3&#xff0c; 重启。 本文详细介绍了图形界面GUI和命令行CLI下如何收集DELL EMC Unity日志的方法和常…

Spring Cloud笔记 —— 什么是Spring Cloud?

引言&#xff1a; 在写这篇博客之前&#xff0c;其实吧&#xff0c;博主很久之前有过一段时间的Spring Cloud的案例项目开发经验&#xff0c;就是一个案例项目开发而已&#xff0c;也说不上有多高大上&#xff0c;那个时候&#xff0c;我其实也是从众而已罢了&#xff0c;毕竟现…

LLM;超越记忆《第 2 部分 》

一、说明 在这篇博客中&#xff0c;我深入研究了将大型语言模型&#xff08;LLM&#xff09;提升到基本记忆之上的数学框架。我们探索了动态上下文学习、连续空间插值及其生成能力&#xff0c;揭示了 LLM 如何理解、适应和创新超越传统机器学习模型。 LLM代表了人工智能的重大飞…

基于相关性的四种机器学习聚类方法

在这篇文章中&#xff0c;基于20家公司的股票价格时间序列数据。根据股票价格之间的相关性&#xff0c;看一下对这些公司进行分类的四种不同方式。 苹果&#xff08;AAPL&#xff09;&#xff0c;亚马逊&#xff08;AMZN&#xff09;&#xff0c;Facebook&#xff08;META&…

TCP简介及特性

1. TCP协议简介 TCP是Transmission Control Protocol的简称&#xff0c;中文名是传输控制协议。它是一种面向连接的、可靠的、基于IP的传输层协议。两个TCP应用之间在传输数据的之前必须建立一个TCP连接&#xff0c;TCP采用数据流的形式在网络中传输数据。TCP为了保证报文传输的…

idea汉化

所有的jetbrains 汉化包下载地址&#xff0c; 包括leda &#xff0c;pycharm /&#xff0c;datagrip 等软件&#xff0c;&#xff0c;所有方法都一样&#xff1a;搜索对应的版本需要的包 下载后&#xff0c;在idea的插件中选择从磁盘加载&#xff0c;然后重启 &#xff0c;即可…

循环队列的结构设计和基本操作的实现(初始化,入队,出队,判空,获取长度,清空,销毁)

目录 1.队列的定义 2.循环队列的设计图示 3.循环队列的结构设计 4.循环队列的实现 5.循环队列的总结 1.队列的定义 和栈相反,队列(queue)是一种先进先出(first in first out,缩写为FIFO)的线性表.它只允许在表的一端进行插入,而在另一端删除元素. 在队列中,允许插入的一…

根文件系统构建-busybox中文支持

一. 简介 根文件系统里面就是一堆的可执行文件和其他文件组成的&#xff1f;难道我们得一个一个的从网上去下载这些文件&#xff1f;显然这是不现实的&#xff01;那么有没有人或者组织专门干这个事呢&#xff1f; 他们负责“收集”这些文件&#xff0c;然后将其打包&#xf…

阿里云域名解析到非默认端口处理方式

1.需配置两条解析记录&#xff0c;如下图 2.第一条配置A记录&#xff0c;ip指向部署服务器 3.第二条配置隐形记录&#xff0c;指向第一条的网址&#xff0c;并附带端口号&#xff0c;最终访问第二条的网址就不用带非默认端口号了。 4.最终浏览器访问

[FC][常见Mapper IRQ研究]

本次IRQ研究了如下表所示Mapper的IRQ操作: 卡带名Mapper号VRC373VRC421,23,25VRC624 & 26VRC785MMC34MMC410MMC55Sunsoft FME-769Namco16319Jaleco SS 8800618RAMBO-164 共计11种Mapper的IRQ操作使用例子 代码内有详细注释, 希望能帮助到感兴趣的人. Mapper控制代码(MMC3…

matplotlib学习

显示两个figure 坐标上刻度修改 plt.xlim() 下标范围 plt.xticks() 替换新的下标 图例显示 散点图 subplot多合一显示

docker部署frp穿透内网

文章目录 &#xff08;1&#xff09;部署frps服务器&#xff08;2&#xff09;部署frpc客户端&#xff08;3&#xff09;重启与访问frp&#xff08;4&#xff09;配置nginx反向代理 &#xff08;1&#xff09;部署frps服务器 docker安装参考文档&#xff1a;docker基本知识 1…

Tmux奇技淫巧

Tmux奇技淫巧 在日常的开发工作中&#xff0c;终端是我们最常用的工具之一。在终端中我们可以调用各种解释器&#xff0c;来执行命令&#xff0c;完成我们的工作。然而&#xff0c;对于只使用终端的默认功能的开发者来说&#xff0c;他们可能会错过一些强大的工具和技巧&#…

【SLAM十四讲-9.3 实践Ceres BA-BAL数据集problem-16-22106-pre.txt分析】

数据集Dubrovnik Dataset 杜布罗夫尼克数据集的链接&#xff1a;Bundle Adjustment in the Large https://grail.cs.washington.edu/projects/bal/ problem-16-22106-pre.txt.bz216 22106 83718&#xff08;这里是第1行&#xff09; 0 0 -3.859900e02 3.871200e02&#x…

Gson的用法详解

一、简介 Gson&#xff08;又称Google Gson&#xff09;是Google公司发布的一个开放源代码的Java库&#xff0c;主要用途为序列化Java对象为JSON字符串&#xff0c;或反序列化JSON字符串成Java对象。 Gson官网&#xff1a;gson Gson源码地址&#xff1a;google/gson 二、依赖…

STM32F407-14.3.8-01强制输出模式

强制输出模式 在输出模式&#xff08;TIMx_CCMRx 寄存器中的 CCxS② 位 00&#xff09;下&#xff0c;可直接由软件将每个输出比较信号&#xff08;OCxREF④ 和 OCx⑥/OCxN⑦&#xff09;强制设置为有效电平或无效电平&#xff0c;而无需考虑输出比较寄存器和计数器之间的任何…