当前位置: 首页 > news >正文

通过查看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的简单理解!

相关文章:

  • Unittest单元测试之unittest用例执行顺序
  • 机器学习笔记 - 基于百度飞桨PaddleSeg的人体分割模型以及TensorRT部署说明
  • 【C++初阶(十)】set、map、multiset、multimap的介绍及使用
  • 【Qt】获取当前系统用户名:9种获取方式
  • 有趣的小算法
  • 可可爱爱的羽绒服,面料是三防的哦
  • 高级算法设计与分析练习1-10
  • matlab基于线性二次调节器(LQR)法实现机器人路径规划可变轨迹跟踪
  • Leetcode 第 110 场双周赛 Problem D 2809. 使数组和小于等于 x 的最少时间(DP+贪心+正难则反)
  • 分布式架构demo
  • IP地理定位技术的服务内容详解
  • windows ce Remote Process Explorer定位程序崩溃地址
  • k8s部署jenkins
  • 算法——滑动窗口
  • FastAPI中如何调用同步函数
  • 【Leetcode】101. 对称二叉树
  • hexo+github搭建个人博客
  • angular2开源库收集
  • docker-consul
  • Java精华积累:初学者都应该搞懂的问题
  • jdbc就是这么简单
  • PHP的Ev教程三(Periodic watcher)
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • 从零开始的无人驾驶 1
  • 漂亮刷新控件-iOS
  • 全栈开发——Linux
  • 删除表内多余的重复数据
  • 我与Jetbrains的这些年
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 怎么把视频里的音乐提取出来
  • 正则表达式小结
  • ​油烟净化器电源安全,保障健康餐饮生活
  • #include<初见C语言之指针(5)>
  • (1)(1.19) TeraRanger One/EVO测距仪
  • (1)(1.9) MSP (version 4.2)
  • (Ruby)Ubuntu12.04安装Rails环境
  • (三)c52学习之旅-点亮LED灯
  • (十五)使用Nexus创建Maven私服
  • (算法二)滑动窗口
  • (原創) 是否该学PetShop将Model和BLL分开? (.NET) (N-Tier) (PetShop) (OO)
  • (转)mysql使用Navicat 导出和导入数据库
  • (轉)JSON.stringify 语法实例讲解
  • .bat批处理(九):替换带有等号=的字符串的子串
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .Net Web项目创建比较不错的参考文章
  • .net打印*三角形
  • .net访问oracle数据库性能问题
  • .NET是什么
  • .NET学习教程二——.net基础定义+VS常用设置
  • .NET与java的MVC模式(2):struts2核心工作流程与原理
  • .net中的Queue和Stack
  • /etc/apt/sources.list 和 /etc/apt/sources.list.d
  • @Autowired @Resource @Qualifier的区别
  • @Data注解的作用