一篇文章快速深入学习ThreadLocal

(P2)PALM软件的基本使用流程 上一篇介绍了PALM在Ubuntu系统上的下载安装及安装后的目录文件结构,这一篇以自带的案例介绍软件的基本操作流程。(请转至example_cbl项目) 输入 声明:官网均有各个变量的说明,以下代码来自软件自带案例 palm的最小输入文件是p3d文件,以下是example_cbl_p3d的详细说明。(文件语言格式Fortran,请自行先学习下语言的基本语法。笔者也是新学者,有非常多的问题,很多参数也不能准确的说出是什么意思,还请不吝赐教,谢谢!!!) !几点说明 !1、该运行文件基本由三大块组成:1)in 阅读详情

前言

最近开始空闲起来了,深入学习系列以及自我提升系列都得提上日程了。本次学习的ThreadLocal,是由JDK提供的一个用于存储每个线程本地副本信息的类,它的编写者就是著名的并发包大神Doug Lea。要想深入学习一个类,首先得先阅读它的官方类注释:

 * This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} instances are typically private
 * static fields in classes that wish to associate state with a thread (e.g.,
 * a user ID or Transaction ID).

翻译过来的意思就是:

这个类用于提供线程本地变量,这些变量和普通的变量不同,因为每个线程通过访问ThreadLocal的get或者
是set方法都会有其独立的、初始化的变量副本。ThreadLocal实例通常是希望将线程独有的状态(例如用户ID、交易ID)
线程中的私有静态字段进行关联,即将线程独有的状态存储到线程中。
* <p>Each thread holds an implicit reference to its copy of a thread-local
 * variable as long as the thread is alive and the {@code ThreadLocal}
 * instance is accessible; after a thread goes away, all of its copies of
 * thread-local instances are subject to garbage collection (unless other
 * references to these copies exist)

这段的意思是:

每个线程都会持有一个指向ThreadLocal变量的隐式引用,只要线程还没有结束,该引用就不会被GC。
但当线程结束后并且其他地方没有对这些副本进行引用,则线程本地实例的所有副本都会被GC。

正文

1. 适用(使用)场景

源码学习总是非常枯燥的,所以得先了解要学习的类能够做什么,适用和使用场景有哪些,这样学起来就更有目的性。经过前文对于ThreadLocal源码类注释的翻译过后,我们大致知道了ThreadLocal的作用,可以概括为两点:

  1. 用于存储线程本地的副本变量,说白了就是为了做到线程隔离。
  2. 用于确保线程安全。

但ThreadLocal的作用不止这两点,带着疑惑我们先看下ThreadLocal有哪些使用场景。

1.1 线程资源持有(线程隔离)

在这里插入图片描述
在WEB程序中,每个线程就是一个session,不同用户访问程序会通过不同的线程来访问,通过ThreadLocal来确保同一个线程的访问获得的用户信息都是相同的,同时也不会影响其他线程的用户信息。所以ThreadLocal可以很好的确保线程之间的隔离性。

1.2 线程资源一致性

在这里插入图片描述
在这里插入图片描述
嗯… 图片有些模糊。

这个场景呢,在JDBC内部都有使用到。在JDBC内部,会通过ThreadLocal来实现 线程资源的一致性。我们都知道,每个HTTP请求都会在WEB程序内部生成一个线程,而每个线程去访问DB的时候,都会从连接池中获取一个Connection连接用于进行数据库交互。那么当一个HTTP请求进来,该请求在程序内部调用了不同的服务,包括搜索服务、下单服务、付款服务等,在这个调用链中每次请求一个服务都需要进行一次数据库交互,那么有一个问题就是如何确保请求过程中和数据库交互的 事务状态一致 的问题,如果同一个请求的调用链中connection都不同,则事务就没法控制了,因此在JDBC中通过了ThreadLocal来确保每次的请求都会和同一个connection进行一一对应,确保一次请求链中都用的同一个connection,这就是 线程资源的一致性。

1.3 线程安全

在这里插入图片描述
基于ThreadLocal存储在Thread中作为本地副本变量的机制,保证每个线程都可以拥有自己的上下文,确保了线程安全。相比于加锁(Synchronize、Lock),ThreadLocal的效率更高。

1.4 分布式计算

在这里插入图片描述
对于分布式计算场景中,即每个线程都计算出结果后,最终通过将ThreadLocal存储的结果取出,并收集。

1.5 在SqlSessionManager中的应用

在SqlSessionManager中,对于SqlSession的存储,就是通过ThreadLocal来进行的。
在这里插入图片描述
可以看到,在getConnection()的时候,实际上就是去从ThreadLocal中去获取连接—SqlSession。
在这里插入图片描述
在这里插入图片描述

1.6 在Spring框架中的TransactionContextHolder中的应用

在Spring框架中的TransactionContextHolder中,也同样使用了ThreadLocal,以一个分布式事务的业务场景来进行分析:
在这里插入图片描述
在淘宝APP中,需要购买某个商品,会涉及交易中台,履约中台。购买一个商品后,会在交易中台去更新订单,同时需要去履约中台进行合约签订。但如果淘宝APP回滚了,则履约中台和交易中台也需要进行业务回滚。对于分布式事务,需要有一个context,即资源上下文,用于存储用户的信息、订单的信息以及来源等,因此在Spring的TransactionContextHolder中,就通过ThreadLocal来存储context。

在这里插入图片描述
以上都是在学习慕课网“求老仙奶我不到P10”老师的《ThreadLocal讲解》的视频中总结的内容,如有侵权请联系删除。PS:老师讲解的非常好,建议小伙伴们都去观看学习一波。

2. 源码学习

上面已经了解到ThreadLocal的适用(使用)场景了,下面就开始枯燥的源码学习了,在学习之前,我们先提出几个疑问:

  1. ThreadLocal是怎么保证了线程隔离的?
  2. ThreadLocal注释中提到的隐式引用是什么?有什么作用?
  3. ThreadLocal为什么要用到隐式引用?而不用强引用?
  4. 据说ThreadLocal会发生内存泄漏?什么情况下会发生内存泄漏?如何避免内存泄漏?
  5. 使用ThreadLocal有什么需要注意的点?
2.1 ThreadLocal内部使用了哪些数据结构?

首先,我们来看下ThreadLocal中几个比较重要的数据结构。

/**
 * 用于ThreadLocal内部ThreadLocalMap数据结构的哈希值,用于降低哈希冲突。
 */
private final int threadLocalHashCode = nextHashCode();

/**
 * 原子操作生成哈希值,初始值为0.
 */
private static AtomicInteger nextHashCode =  new AtomicInteger();

/*
 * 用于进行计算出threadLocalHashCode的哈希值。
 */
private static final int HASH_INCREMENT = 0x61c88647;

/**
 * 返回下一个哈希值,让哈希值散列更均匀。
 */
private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
}

下面将是ThreadLocal最终要的一个数据结构:ThreadLocalMap

/**
 * ThreadLocalMap其实就是一个用于ThreadLocal的自定义HashMap,它和HashMap很像。在其内部有一个自定义的Entry类,
 * 并且有一个Entry数组来存储这个类的实例对象。类似于HashMap,ThreadLocalMap同样的拥有初始大小,拥有扩容阈值。
 */
static class ThreadLocalMap {
	/*
	 *  可以看到,Entry类继承了WeakReference类,它的含义是弱引用,即JVM进行GC时,无论当前内存是否够用,
	 *  都会把被WeakReference指向的对象回收掉。
	 */
	static class Entry extends WeakReference<ThreadLocal<?>> {   
	    /** The value associated with this ThreadLocal. */       
	    Object value;
	
	    Entry(ThreadLocal<?> k, Object v) {                      
	        super(k);                                            
	        value = v;                                           
	    }                                                        
	}
	// ThreadLocalMap的初始大小
	private static final int INITIAL_CAPACITY = 16
	                                                
	// 用于存储Entry的数组                                                                               
	private Entry[] table;
	                                      
	private int size = 0;
	
	// 扩容阈值,扩容阈值为初始大小值的三分之二。           
	private int threshold; // Default to 0
	                                    
	private void setThreshold(int len) {          
	    threshold = len * 2 / 3;                  
	}
	                                       
	private static int nextIndex(int i, int len) {
	    return ((i + 1 < len) ? i + 1 : 0);       
	}
	                                     
	private static int prevIndex(int i, int len) {
	    return ((i - 1 >= 0) ? i - 1 : len - 1);  
	}                                                                                                         
}

那么对于ThreadLocalMap中,Entry为什么要继承WeakReference,而不是其他的Reference?这里由于篇幅原因,就不加以介绍,并且网上已经有很多优秀的分析博文,可以看下末文的引用,这里就不继续深入了,简单总结以下几点原因:

  1. 是为了再Thread线程在执行过程中,key能够被GC掉,从而在需要彻底GC掉ThreadLocalMap时,只需要调用ThreadLocal的remove方法即可。
  2. 如果是用的强引用,虽然Entry到Thread不可达,但是和Value还有强引用的关系,是可达的,所以无法被GC掉。

虽然Entry使用的是WeakReference虚引用,但JVM只是回收掉了ThreadLocalMap中的key,但是value和key是强引用的(value也会引用null),所以value是无法被回收的,所以如果线程执行时间非常长,value持续不GC,就有内存溢出的风险。所以最好的做法就是调用ThreadLocal的remove方法,把ThreadLocal.ThreadLocalMap给清除掉。

2.2 源码分析

先看下Thread类的源码,在Thread类中,定义了两个ThreadLocalMap变量
在这里插入图片描述
这里就可以发现,ThreadLocalMap变量定义在Thread中,因而每个Thread都拥有自己的ThreadLocalMap变量,互不影响,因而实现了线程隔离性。

这里有一个inheritableThreadLocals,作用是用于父子线程间ThreadLocal变量的传递。详细的关于inheritableThreadLocals的分析可以学习下博文InheritableThreadLocal详解。

下面回到关于ThreadLocal源码的介绍,先看看set()和get()方法源码:

	// ThreadLocal中的set()方法
	public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
        	// 将当前线程传入,作为ThreadLocalMap的引用,创建出ThreadLocalMap
            createMap(t, value);
    }
    
    // ThreadLocalMap中的set()方法
	private void set(ThreadLocal<?> key, Object value) {
			// 初始化Entry数组
            Entry[] tab = table;
            int len = tab.length;
            // 通过取模计算出索引值
            int i = key.threadLocalHashCode & (len-1);

			// 如果ThreadLocalMap中tab的槽位已经被使用了,则寻找下一个索引位,i=nextIndex(i, len)
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) {
                    e.value = value;
                    return;
                }
				// 如果key引用被回收了,则用新的key-value来替换,并且删除无用的Entry
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            // 清楚哪些get()为空的对象,然后进行rehash。
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
	public T get() {
		// 获取当前线程
        Thread t = Thread.currentThread();
        // 获取线程t中的ThreadLocalMap
        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;
            }
        }
        // 如果没有获取到ThreadLocalMap,则初始化一个ThreadLocalMap
        return setInitialValue();
    }
	ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    // 初始化
	private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
        	// 把线程存放到当前线程的ThreadLocalMap中
            createMap(t, value);
        return value;
    }

知道怎么存储以及获取ThreadLocal之后,还要知道怎么清除ThreadLocal,防止内存泄漏,下面看下remove()源码:

	// ThreadLocal的remove()方法
	public void remove() {
		// 获取当前线程中的ThreadLocalMap
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

	// ThreadLocalMap中的remove()方法
	private void remove(ThreadLocal<?> key) {
            Entry[] tab = table;
            int len = tab.length;
            // 通过取模获取出索引位置,
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
               
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

	/**
	 *  清除没用的槽位以及null插槽,并且对其进行重新散列。
	 */
	private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            // 将插槽位置的键和值都设置为null
            tab[staleSlot].value = null;
            tab[staleSlot] = null;
            size--;

            // 遇到null的插槽,重新散列计算哈希值。
            Entry e;
            int i;
            for (i = nextIndex(staleSlot, len);
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                ThreadLocal<?> k = e.get();
                if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } else {
                    int h = k.threadLocalHashCode & (len - 1);
                    if (h != i) {
                        tab[i] = null;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until
                        // null because multiple entries could have been stale.
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        tab[h] = e;
                    }
                }
            }
            return i;
        }

3. 总结

整片文章,先介绍了ThreadLocal的适用场景,然后再由此带着疑问深入学习了ThreadLocal源码,不过ThreadLocal源码中,其实还有许多没有挖掘完的细节,这部分接下来会持续的深入分析并学习,然后再过来跟下本篇博文。

3.1 ThreadLocal的适用场景

① 线程资源持有(线程隔离)
② 线程资源一致性
③ 线程安全
④ 分布式计算

3.2 哪些开源框架、源码使用到了ThreadLocal

① JDBC获取Connection相关源码
② MyBatis中的SqlSessionManager相关源码
③ Spring框架中的TransactionContextHolder相关源码

3.3 关于内存泄漏

由于ThreadLocalMap的Entry继承了WeakReference,所以只要JVM发起了GC,就会回收掉Entry的键,导致当线程持续运行时,ThreadLocal中value值增多,并且没法对其进行GC,所以导致内存泄漏,因此需要调用其remove方法,避免内存泄漏。

3.4 其他

有哪里分析总结不对的地方,欢迎各位读者及时指出。

本篇博文已收录至 GITHUB中,地址如下:https://github.com/coderbruis/JavaSourceCodeLearning

引用

  1. 求老仙奶我不到P10 老师的ThreadLocal讲解
  2. ThreadLocal分析其弱引用和可能引起的内存泄漏
  3. InheritableThreadLocal详解
CAN 比特率和采样点计算 架构根据输入频率和比特率自动计算。 阅读详情

相关推荐

养龙虾经验:OpenClaw用户必看的3个“省token”救命配置

本文分享了3个优化OpenClaw使用成本的配置方案:1)启用上下文压缩功能,可选择全局或单个代理设置,支持safeguard/adaptive两种压缩模式;2)修改SOUL.md提示词,要求模型回答简洁直接;3)限制对话历史长度。方案一效果最佳,可减少长对话的token消耗,但可能影响连贯性。三种方案可单独或组合使用,需根据实际场景调整参数。文章最后邀请读者分享使用痛点,将针对高频问题撰写解决方案。

wpplvhq的专栏 1416

八、使用ThreadLocal绑定SqlSession优化思想

ThreadLocal:线程容器,给线程绑定一个 Object 内容,后只要线程不变,可以随时取出. 注意:如果改变了线程,无法取出内容. 代码示例: final ThreadLocal&lt;String&gt; threadLocal = newThreadLocal&lt;&gt;(); threadLocal.set("测试"); new Thread(){ public...

DOONDO的技术窝 1587

Android开发Intent详解

本文详解Android开发Intent。

qq_30725967的博客 2412

事务回滚原理

在前文单测类注入中我们知道.JUnit提供了一些监听器,允许 当单测方法执行时候去对单测上下文进行调整。所以呢事务回滚也是基于 这里的特性完成的。基于SpringBoot 2.1.x版本分析 # 源码分析 Spring中为了适配不通的数据库,提供了事务平台的概念。 PlatformTransactionManager 只要实现了该接口 就允许对事务进行控制。具体事务的控制是通过工具类来处理的。 ...

西魏陶渊明的博客 680

车机破解避坑指南:为什么你的CarPlay盒子不识别?从USB设备ID解析兼容性问题

本文深入解析车机USB设备兼容性问题,特别是CarPlay盒子不识别的原因,从硬件ID到识别机制全面剖析。通过ADB工具和root权限操作,提供详细的破解方案和兼容性测试结果,帮助用户解决车机与第三方设备的连接问题。

weixin_29175469的博客 602

最大似然估计、梯度下降、EM算法、坐标上升

主要讲解的是机器学习中参数学习算法之间的区别,以及应用的场景。 最大似然估计: 其中目标函数是对数似然函数。为了求目标函数取最大值时的theta。有两个关机键步骤,第一个是对目标函数进行求导,第二个是另导数等于0,求解后直接得到最优theta。两个步骤缺一不可。 梯度下降: 对目标函数进行求导,利用导函数提供的梯度信息,使参数往梯度下降最快的方向移动一小步,来更新参数。为什

tzl2093的博客 5141

ThreadLocal不仅要应付面试,更要真的理解,真的会用

ThreadLocal,千万不要一看就会,一用就废

zhangsir的博客 7851

人像抠图 + OpenGL ES 还能这样玩?没想到吧(附带源码)

OpenGL ES 利用抠图算法实现人像留色。

字节流动 3万+

@DSTransactional注解原理

dstrsanctional注解原理

wangchengqi1997的博客 1万+

智能挂锁软件需求规格说明书(SRS)

智能挂锁系统SmartPadlockV1.0是一款融合蓝牙通信、指纹识别和远程控制技术的便携式安防设备。系统由设备端、移动App和云平台三部分组成,支持蓝牙配对(10米内)、指纹识别、OTP一次性密码等多种开锁方式,响应时间<1.5秒。产品采用SM2/SM4加密技术保障安全,日均20次使用下续航达180天,兼容iOS13+/Android8.0+系统。功能包括设备绑定、远程授权、开锁记录查询和OTA固件升级,符合个人信息保护法规要求,适用于仓储、快递柜等多场景应用。

moton2017的博客 2110

Android 12 系统横屏时,开机动画有两秒显示不全

平板系统默认横屏时,开机会旋转90度,显示的分辨率WxH,会变成HxW.但又会很快被SurfaceFlinger的SurfaceFlinger::processDisplayChanged 娇正。所以为了保险,可以在开机动画没结束之前,禁止旋转事件。在Android 12 产品定制化开发中,由于系统默认方向为竖屏,平板电脑要横屏显示。系统在开机时,有时因为旋转,会出现动画只显示一部分。

448313096@qq.com 1174

分布式事务

补偿事务(TCC):TCC 是一种基于补偿机制的分布式事务解决方案,它在执行每个参与者的操作之前,先进行一次检查,如果检查通过,则执行业务操作,否则执行回滚操作。Sagas:Sagas 是最近比较流行的一种分布式事务解决方案,它将一个大事务划分为多个小的本地事务,并通过补偿机制来保证整个分布式事务的一致性。两阶段提交(2PC):这是一种最常见的分布式事务协议,它通过预提交、正式提交和回滚三个阶段来实现分布式事务的原子性和一致性。安全问题:由于涉及多个节点的通信和数据传输,分布式事务的安全性需要得到保证。

厚积薄发 474

YOLOv11 改进 - 注意力机制 | EMA (Efficient Multi-Scale Attention) 高效多尺度注意力:跨空间学习与多分支协同增强特征表征,优化多尺度目标检测

本文介绍了高效多尺度注意力(EMA)模块及其在YOLOv11中的结合应用。现有注意力机制在通道维度缩减时可能影响深度视觉表示,EMA模块通过结合通道和空间信息、采用多尺度并行子网络结构等创新点,实现了高效的多尺度注意力机制。其基本原理包括通道和空间注意力结合、多尺度并行子网络设计等。在多个流行基准数据集上的实验表明,该模块能有效提高特征表示能力。我们将EMA集成进YOLOv11,实验证明改进后的模型在目标检测任务中性能优越,具有广泛应用前景

魔改工程师的博客 1292

【Java】一篇文章学会 ThreadLocal

本篇文章会从源码的角度分析 ThreadLocal。文章内容不长,阅读时间大致为:4 分钟。是 Java 中提供的一种用于在多线程环境中存储线程私有变量的工具。它允许每个线程都能有自己的、独立于其他线程的变量副本,确保线程之间的数据隔离,避免数据竞争和状态共享问题。看下面图来加深理解(※※※)一个线程对应一个 ThreadLocalMap,ThreadLocalMap 中存放一个 Entry[] 数组,Entry 的结构是:以 ThreadLocal 对象为 key, Object 为 value。

2401_82656016的博客 2090

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

【并发基础】一篇文章带你快速掌握ThreadLocal及其原理

一篇文章快速带你了解ThreadLocal的底层原理和面试必考点,带你分析ThreadLocal底层源码以及应用场景。

小七的博客 526

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

java中threadlocal_快速了解Java中ThreadLocal类

最近看Android FrameWork层代码,看到了ThreadLocal这个类,有点儿陌生,就翻了各种相关博客一一拜读;自己随后又研究了一遍源码,发现自己的理解较之前阅读的博文有不同之处,所以决定自己写篇文章说说自己的理解,希望可以起到以下作用:- 可以疏通研究结果,加深自己的理解;- 可以起到抛砖引玉的作用,帮助感兴趣的同学疏通思路;- 分享学习经历,同大家一起交流和学习。一、 Thread...

weixin_29920889的博客 427

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

快速掌握并发编程---深入学习ThreadLocal

考试题只有一套,老师把考试题打印出多份,发给每位考生,然后考生各自写各自的试卷。考生之间不能相互交头接耳(会当做作弊)。各自写出来的答案不会影响他人的分数。注意:考试题、考生、试卷。 用代码来实现: 输出 :字面意思为线程本地或者本地线程。但是其实真正含义并非如此,真正的含义是线程本地变量(副本)。是版本的时候引入的,本文是基于版本进行讲解的。上面考试场景中的几个关键点我们这么可以这么理解:可以理解为每个线程想绑定自己的东西,相互不受干扰。比如上面的考试场景,考试题大家都是一样的。但是考试题进

weixin_70730532的博客 310

恐怖!ThreadLocal引起的一次线上事故

其他的内容都可以按照路线图里面整理出来的知识点逐一去熟悉,学习,消化,不建议你去看书学习,最好是多看一些视频,把不懂地方反复看,学习了一节视频内容第二天一定要去复习,并总结成思维导图,形成树状知识网络结构,方便日后复习。这里还有一份很不错的《Java基础核心总结笔记》,特意跟大家分享出来目录:部分内容截图:《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取![外链图片转存中…(img-2ZW8OjCc-1713686339128)]

2401_84048542的博客 857

ThreadLocal源码学习笔记

ThreadLocal源码学习笔记欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习

qq_43980662的博客 230

并发容器之ThreadLocal详解

在多线程编程中通常解决线程安全的问题我们会利用synchronzed或者lock控制线程对临界区资源的同步顺序从而解决线程安全的问题,但是这种加锁的方式会让未获取到锁的线程进行阻塞等待,很显然这种方式的时间效率并不是很好。线程安全问题的核心在于多个线程会对同一个临界区共享资源进行操作,那么,如果每个线程都使用自己的“共享资源”,各自使用各自的,又互相不影响到彼此即让多个线程间达到隔离的状态,这样就不会出现线程安全的问题。事实上,这就是一种“空间换时间”的方案,每个线程都会都拥有自己的“共享资源”无疑内存会大

m0_69378598的博客 734

快速搞懂ThreadLocal实现原理

文章目录 一、ThreadLocal使用案例 二、ThreadLocal类的实现原理 2.1 核心方法set() 2.2 核心方法get() 2.3 核心方法remove() 三、ThreadLocal.ThreadLocalMap结构分析 四、ThreadLocal内存泄漏问题 参考资料 维持线程封闭性可以通过Ad-hoc线程封闭、栈封闭来实现,一种更加规范的方法是使用ThreadLocal类。...

hellozhxy的博客 519

大神们是怎么使用ThreadLocal的?

点击↑上方↑蓝色“编了个程”关注我~每周至少一篇原创文章这是本公众号的第 18 篇原创文章这篇文章是关于ThreadLocal的第三篇文章。本文将挑选一些主流的Java开源框架,从源码上...

yasinshaw的博客 418

ThreadLocal的使用以及底层原理

​​​if (map!= null) {可以发现,当调用ThreadLocal类中的方法时,首先都需要获取当前Thread t,然后再获取t的ThreadLocalMap map,最后通过操作该map实现一系列操作。并且ThreadLocal类中存在一个内部类。

weixin_45489847的博客 586

ThreadLocal个人理解

    为了加深理解,将最近对ThreadLocal的了解和源码分析记录总结一下。    ThreadLocal可以为线程提供局部变量。使用ThreadLocal的get()方法,可以在当前线程能够访问的类和方法中,得与当前线程相关联的变量值。不过,线程局部变量并不是由ThreadLocal对象保存维护的,而是保存在相关联的线程中。ThreadLocal对象只是作为一个key,为当前线程提供了查找局部变量的线索。也就是说,不同的线程,以相同的ThreadLocal对象作为key,可以查找到相关联的变量值。下

mypop的专栏 1393

ThreadLocal分析

每个线程都会有属于自己的本地内存,在堆中的变量在被线程使用的时候会被复制一个副本线程的本地内存中,当线程修改了共享变量之后就会通过JMM管理控制写会到主内存中。很明显,在多线程的场景下,当有多个线程对共享变量进行修改的时候,就会出现线程安全问题,即数据不一致问题。常用的解决方法是对访问共享变量的代码加锁(synchronized或者Lock)。但是这种方式对性能的耗费比较大。ThreadLocal意为,用于解决多线程并发时访问共享变量的问题。

qq_37436172的博客 320

ThreadLocal 对象的使用

ThreadLocal

Lucy_Leezhi 1163
上一篇: 深入分析 Java I/O 的工作机制
下一篇: 深入浅出SpringBoot源码——SpringFactoriesLoader
CoderBruis
博客等级 码龄10年 4444粉丝 102原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值