【Interview】深入理解ThreadLocal源码

2024-05-13 07:58

本文主要是介绍【Interview】深入理解ThreadLocal源码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

  • ThreadLocal 是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰。在高并发场景下,可以实现无状态的调用,适用于各个线程不共享变量值的操作。
  • 内部使用静态内部类ThreadLocalMap存储每个线程变量副本的方法,key存储的是当前线程的ThreadLocal对象,value就是当前ThreadLocal对应的线程变量的的副本值。

提供方法

  • T get() 返回此线程局部变量的当前线程副本中的值。
  • protected T initialValue() 返回此线程局部变量的当前线程的“初始值”。线程第一次使用 get() 方法访问变量时将调用此方法,但如果线程之前调用了 set(T) 方法,则不会对该线程再调用 initialValue 方法。通常,此方法对每个线程最多调用一次,但如果在调用 get() 后又调用了 remove(),则可能再次调用此方法。
  • void remove() 移除此线程局部变量当前线程的值。
  • void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值。

怎嘛使用

public class ThreadLocalTest {ThreadLocal<Integer> threadLocal=new ThreadLocal<Integer>(){//返回此线程局部变量的当前线程的“初始值”。@Overrideprotected Integer initialValue() {return 0;}};//   返回此线程局部变量的当前线程副本中的值。public int get(){//将此线程局部变量的当前线程副本中的值设置为指定值。threadLocal.set(threadLocal.get()+1);return  threadLocal.get();}public static void main(String[] args) {ThreadLocalTest test=new ThreadLocalTest();new Thread(()->{for (int i = 0; i <3 ; i++) {int state=test.get();System.out.println(Thread.currentThread().getName()+"获取值:"+state);}}).start();new Thread(()->{for (int i = 0; i <3 ; i++) {int state=test.get();System.out.println(Thread.currentThread().getName()+"获取值:"+state);}}).start();}
}//输出Thread-0获取值:1
Thread-0获取值:2
Thread-0获取值:3
Thread-1获取值:1
Thread-1获取值:2
Thread-1获取值:3

源码分析

set()方法

    public void set(T value) {//记录当前线程Thread t = Thread.currentThread();//获取当前线》的ThreadLocalMap ThreadLocalMap map = getMap(t);if (map != null)//ThreadLocalMap  不为空则直接设置当前变成的副本值,map.set(this, value);else//创建ThreadLocalMap  key当前线程对象,value:副本值createMap(t, value);}

ThreadLocalMap 内部类

    static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {/** 与此ThreadLocal关联的值.  */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}
  • 源码中可以看出 ThreadLocalMap 依靠Entry 来存储ThreadLocal和副本值,key就是ThreadLocalvalue就是ThreadLocal的变量副本值。Entry 集成WeakReference,说明是一个弱引用关系。当一个对象仅仅被弱引用指向, 而没有任何其他强引用指向的时候, 如果这时GC运行, 那么这个对象就会被回收,不论当前的内存空间是否足够,这个对象都会被回收。

getMap方法

	//获取与ThreadLocal关联的Thread中的ThreadLocal。ThreadLocalMap getMap(Thread t) {return t.threadLocals;}
  • ThreadLocal是包含在Thread类中的

ThreadLocalMapset方法

        private void set(ThreadLocal<?> key, Object value) {Entry[] tab = table;int len = tab.length;//计算ThreadLocal 散列值 找到存储位置int i = key.threadLocalHashCode & (len-1);//利用线性探测法找到合适的存储位置for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();//如果找到的k和传入的key相等,说明存在,覆盖更新即可if (k == key) {e.value = value;return;}// key == null,但是存在值(因为此处的e != null),说明之前的ThreadLocal对象已经被回收了if (k == null) {// 替换之前的的元素replaceStaleEntry(key, value, i);return;}}//不存在对应key的实例,则创建一个新的tab[i] = new Entry(key, value);//增加容量大小int sz = ++size;//        // 如果没有清理陈旧的 Entry 并且数组中的元素大于了阈值,则进行 rehashif (!cleanSomeSlots(i, sz) && sz >= threshold)//整表格的大小。 首先扫描整个表,删除过时的条目。 如果这不足以缩小表的大小,则将表大小加倍。rehash();}

get()操作

    public T get() {// 记录当前访问线程Thread t = Thread.currentThread();//获取当前线程的ThreadLocalMap对象//Thread的    ThreadLocal.ThreadLocalMap threadLocals参数ThreadLocalMap map = getMap(t);if (map != null) {//存在ThreadLocalMap 则获取相对应的EntryThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")//ThreadLocalMap内部类中有一个Entry内部类//依靠Entry`来存储`ThreadLocal`和副本值。直接以ThreadLocal为key获取副本值T result = (T)e.value;return result;}}return setInitialValue();}//getEntry方法private Entry getEntry(ThreadLocal<?> key) {//计算ThreadLocal的在数组中的位置,采用了开放定址法int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];//存在则返回if (e != null && e.get() == key)      return e;else//不在在的操作return getEntryAfterMiss(key, i, e);}/**key:线程本地对象i:哈希表的索引e: 对应的Entry*/private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {Entry[] tab = table;int len = tab.length;while (e != null) {ThreadLocal<?> k = e.get();if (k == key)return e;if (k == null)//key == null,有利于GC回收,能够有效地避免内存泄漏。expungeStaleEntry(i);elsei = nextIndex(i, len);e = tab[i];}return null;}

参考:http://cmsblogs.com/?p=2442

这篇关于【Interview】深入理解ThreadLocal源码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/985123

相关文章

SpringBoot中使用 ThreadLocal 进行多线程上下文管理及注意事项小结

《SpringBoot中使用ThreadLocal进行多线程上下文管理及注意事项小结》本文详细介绍了ThreadLocal的原理、使用场景和示例代码,并在SpringBoot中使用ThreadLo... 目录前言技术积累1.什么是 ThreadLocal2. ThreadLocal 的原理2.1 线程隔离2

深入解析Spring TransactionTemplate 高级用法(示例代码)

《深入解析SpringTransactionTemplate高级用法(示例代码)》TransactionTemplate是Spring框架中一个强大的工具,它允许开发者以编程方式控制事务,通过... 目录1. TransactionTemplate 的核心概念2. 核心接口和类3. TransactionT

深入理解Apache Airflow 调度器(最新推荐)

《深入理解ApacheAirflow调度器(最新推荐)》ApacheAirflow调度器是数据管道管理系统的关键组件,负责编排dag中任务的执行,通过理解调度器的角色和工作方式,正确配置调度器,并... 目录什么是Airflow 调度器?Airflow 调度器工作机制配置Airflow调度器调优及优化建议最

Go中sync.Once源码的深度讲解

《Go中sync.Once源码的深度讲解》sync.Once是Go语言标准库中的一个同步原语,用于确保某个操作只执行一次,本文将从源码出发为大家详细介绍一下sync.Once的具体使用,x希望对大家有... 目录概念简单示例源码解读总结概念sync.Once是Go语言标准库中的一个同步原语,用于确保某个操

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06