前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >12.ThreadLocal的那点小秘密

12.ThreadLocal的那点小秘密

作者头像
阿珍
发布于 2023-02-01 09:40:52
发布于 2023-02-01 09:40:52
27200
代码可运行
举报
运行总次数:0
代码可运行

好久不见,不知道大家新年过得怎么样?有没有痛痛快快得放松?是不是还能收到很多压岁钱?好了,话不多说,我们开始今天的主题:ThreadLocal

我收集了4个面试中出现频率较高的关于ThreadLocal的问题:

  • 什么是ThreadLocal?什么场景下使用ThreadLocal?
  • ThreadLocal的底层是如何实现的?
  • ThreadLocal在什么情况下会出现内存泄漏?
  • 使用ThreadLocal要注意哪些内容?

我们先从一个“谣言”开始,通过分析ThreadLocal的源码,尝试纠正“谣言”带来的误解,并解答上面的问题。

流传已久的“谣言”

很多文章都在说“ThreadLocal通过拷贝共享变量的方式解决并发安全问题”,例如:

这种说法并不准确,很容易让人误解为ThreadLocal会拷贝共享变量。来看个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

public static void main(String[] args) throws InterruptedException {
	for (int i = 0; i < 1000; i++) {
		new Thread(() -> {
            try {
	            System.out.println(DATE_FORMAT.parse("2023-01-29"));
            } catch (ParseException e) {
	            e.printStackTrace();
	        }
	    }).start();
	}
}
复制代码

我们知道,多线程并发访问同一个DateFormat实例对象会产生严重的并发安全问题,那么加入ThreadLocal是不是能解决并发安全问题呢?修改下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**  
 * 第一种写法  
 */
private static final ThreadLocal<DateFormat> DATE_FORMAT_THREAD_LOCAL = new ThreadLocal<>() {
	@Override
    protected DateFormat initialValue() {
        return DATE_FORMAT;
    }
};

public static void main(String[] args) throws InterruptedException {
	for (int i = 0; i < 1000; i++) {
		new Thread(() -> {
            try {
	            System.out.println(DATE_FORMAT_THREAD_LOCAL.get().parse("2023-01-29"));
            } catch (ParseException e) {
	            e.printStackTrace();
	        }
	    }).start();
	}
}
复制代码

估计会有很多小伙伴会说:“你这么写不对!《阿里巴巴Java开发手册》中不是这么用的!”。把书中的用法搬过来:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**  
 * 第二种写法  
 */
private static final ThreadLocal<DateFormat> DATE_FORMAT_THREAD_LOCAL = new ThreadLocal<>() {
	@Override
    protected DateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd");
    }
};
复制代码

Tips:代码小改了一下~~

我们来看两种写法的差别:

  • 第一种写法,ThreadLocal#initialValue时使用共享变量DATE_FORMAT
  • 第二种写法,ThreadLocal#initialValue创建SimpleDateFormat对象

按照“谣言”的描述,第一种写法会拷贝DATE_FORMAT的副本提供给不同的线程使用,但从结果上来看ThreadLocal并没有这么做。

有的小伙伴可能会怀疑是因为DATE_FORMAT_THREAD_LOCAL线程共享导致的,但别忘了第二种写法也是线程共享的。

到这里我们应该能够猜到,第二种写法中每个线程会访问不同的SimpleDateFormat实例对象,接下来我们通过源码一探究竟。

ThreadLocal的实现

除了使用ThreadLocal#initialValue外,还可以通过ThreadLocal#set添加变量后再使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<>();
threadLocal.set(new SimpleDateFormat("yyyy-MM-dd"));
System.out.println(threadLocal.get().parse("2023-01-29"));
复制代码

Tips:这么写仅仅是为了展示用法~~

使用ThreadLocal非常简单,3步就可以完成:

  • 创建对象
  • 添加变量
  • 取出变量

无参构造器没什么好说的(空实现),我们从ThreadLocal#set开始。

ThreadLocal#set的实现

ThreadLocal#set的源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void set(T value) {,
	Thread t = Thread.currentThread();
	
	// 获取当前线程的ThreadLocalMap
	ThreadLocalMap map = getMap(t);

	if (map != null) {
		// 添加变量
		map.set(this, value);
	} else {
		// 初始化ThreadLocalMap
		createMap(t, value);
	}
}
复制代码

ThreadLocal#set的源码非常简单,但却透露出了不少重要的信息:

  • 变量存储在ThreadLocalMap中,且与当前线程有关;
  • ThreadLocalMap应该类似于Map的实现。

接着来看源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ThreadLocal<T> {
	ThreadLocalMap getMap(Thread t) {
		return t.threadLocals;
	}
	
	void createMap(Thread t, T firstValue) {
		t.threadLocals = new ThreadLocalMap(this, firstValue);
	}
}

public class Thread implements Runnable {
	ThreadLocal.ThreadLocalMap threadLocals = null;
}
复制代码

很清晰的展示出ThreadLocalMap与Thread的关系:ThreadLocalMap是Thread的成员变量,每个Thread实例对象都拥有自己的ThreadLocalMap

另外,还记得在关于线程你必须知道的8个问题(上)提到Thread实例对象与执行线程的关系吗?

如果从Java的层面来看,可以认为创建Thread类的实例对象就完成了线程的创建,而调用Thread.start0可以认为是操作系统层面的线程创建和启动。

可以近似的看作是:Thread实例对象≈执行线程Thread实例对象\approx执行线程Thread实例对象≈执行线程。也就是说,属于Thread实例对象的ThreadLocalMap也属于每个执行线程

基于以上内容,我们好像得到了一个特殊的变量作用域:属于线程

Tips

  • 实际上属于线程也即是属于Thread实例对象,因为Thread是线程在Java中的抽象;
  • ThreadLocalMap属于线程,但不代表存储到ThreadLocalMap的变量属于线程。

ThreadLocalMap的实现

ThreadLocalMap是ThreadLocal的内部类,代码也不复杂:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ThreadLocal<T> {

	private final int threadLocalHashCode = nextHashCode();
	
	static class ThreadLocalMap {
	
		static class Entry extends WeakReference<ThreadLocal<?>> {
		
			Object value;
			
			Entry(ThreadLocal<?> k, Object v) {
				super(k);
				value = v;
			}
		}
		
		private Entry[] table;
		
		private int size = 0;
		
		private int threshold;
		
		private void setThreshold(int len) {
			threshold = len * 2 / 3;
		}
		
		ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
			table = new Entry[INITIAL_CAPACITY];
			int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
			table[i] = new Entry(firstKey, firstValue);
			size = 1;
			setThreshold(INITIAL_CAPACITY);
		}
	}
}
复制代码

仅从结构和构造方法中已经能够窥探到ThreadLocalMap的特点:

  • ThreadLocalMap底层存储结构是Entry数组;
  • 通过ThreadLocal的哈希值取模定位数组下标;
  • 构造方法添加变量时,存储的是原始变量

很明显,ThreadLocalMap是哈希表的一种实现,ThreadLocal作为Key,我们可以将ThreadLocalMap看做是“简版”的HashMap。

Tips

  • 本文不讨论哈希表实现中处理哈希冲突,数组扩容等问题的方式;
  • 也不需要关注ThreadLocalMap#setThreadLocalMap#getgetEntry的实现;
  • 与构造方法一样,ThreadLocalMap#set中存储的是原始变量

到目前为止,无论是ThreadLocalMap#set还是ThreadLocalMap的构造方法,都是存储原始变量,没有任何拷贝副本的操作。也就是说,想要通过ThreadLocal实现变量在线程间的隔离,就需要手动为每个线程创建自己的变量

ThreadLocal#get的实现

ThreadLocal#get的源码也非常简单:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public T get() {
	Thread t = Thread.currentThread();
	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;
		}
	}
	return setInitialValue();
}
复制代码

前面的部分很容易理解,我们看map == null时调用的ThreadLocal#setInitialValue方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private T setInitialValue() {
	T value = initialValue();
	Thread t = Thread.currentThread();
	ThreadLocalMap map = getMap(t);
	
	if (map != null) {
		map.set(this, value);
	} else {
		createMap(t, value);
	}
	
	if (this instanceof TerminatingThreadLocal) {
		TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
	}
	return value;
}
复制代码

ThreadLocal#setInitialValue方法几乎和ThreadLocal#set一样,但变量是通过ThreadLocal#initialValue获得的。如果是通过ThreadLocal#initialValue添加变量,在第一次调用ThreadLocal#get时将变量存储到ThreadLocalMap中。

ThreadLocal的原理

好了,到这里我们已经可以构建出对ThreadLocal比较完整的认知了。我们先来看ThreadLocal,ThreadLocalMap和Thread三者之间的关系:

可以看到,ThreadLocal是作为ThreadLocalMap中的Key的,而ThreadLocalMap又是Thread中的成员变量,属于每一个Thread实例对象。忘记ThreadLocalMap是ThreadLocal的内部类这层关系,整体结构就会非常清晰。

创建ThreadLocal对象并存储数据时,会为每个Thread对象创建ThreadLocalMap对象并存储数据,ThreadLocal对象作为Key。在每个Thread对象的生命周期内,都可以通过ThreadLocal对象访问到存储的数据。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
【如何看待“低代码”开发平台的兴起?】效率提升还是质量隐忧?
近年来,“低代码”开发平台如雨后春笋般涌现,承诺让非专业人士也能快速构建应用程序。这种新兴技术正在挑战传统软件开发模式,引发了IT行业的广泛讨论。低代码平台是提高效率的利器,还是降低了编程门槛导致质量下降?它会改变开发者的工作方式吗?让我们一起探讨低代码开发的机遇与挑战。
Francek Chen
2025/01/22
1300
【如何看待“低代码”开发平台的兴起?】效率提升还是质量隐忧?
【KPaaS洞察】低代码开发平台如何降低企业IT成本?
2025年,低代码开发平台市场正经历强劲增长,据全球技术研究公司ISG信息服务集团称,全球低代码无代码开发平台市场价值近150亿美元,预计未来五年将增长四倍。
KPaaS集成扩展
2025/02/21
1460
【KPaaS洞察】低代码开发平台如何降低企业IT成本?
低代码开发平台:技术概览、效率与质量的权衡及挑战与机遇
低代码开发平台市场近年来快速增长,吸引了众多企业和开发者的关注。国内外出现了众多优秀的低代码平台,如OutSystems、Mendix、Microsoft Power Apps,以及国内的织信Informat、钉钉宜达、炎黄盈动等。这些平台各有特色,适用于不同的开发场景和需求。
倔强的石头_
2024/12/06
1810
低代码开发平台:技术概览、效率与质量的权衡及挑战与机遇
低代码平台的优缺点
低代码工具大大减少了开发者需要编写的代码量,通过增加可复用代码和组件的数量来帮助企业适应发展需求,设计和部署自定义应用程序以紧跟市场趋势。低代码平台提供与主要数据库和应用程序的直接集成,将操作和命令背后的代码抽象化,使 IT 人员能够在其现有的数据源(数据库、API、其他 SaaS 应用程序)之上进行应用程序的开发,从而省去数周或数月的开发时间,同时还具有高度的可扩展性。不仅如此,低代码平台还提供可拖拽式的组件库,让后端工程师也能轻松完成前端设计。
码匠Majiang
2023/01/18
1.4K0
低代码平台的优缺点
企业需要进行信息化改革,有哪些好用的信息化管理系统推荐?
前几天遇到一位朋友提问,他说他们公司目前需要进行信息化改革,问我有哪些好用的信息化管理系统推荐?并附上了几点要求:
informat低代码
2022/11/22
8520
企业需要进行信息化改革,有哪些好用的信息化管理系统推荐?
低代码工具优缺点
随着软件开发对效率、敏捷性和交付速度的要求比以往任何时候都高,企业发现他们需要数字化转型来获得编写代码方面的优势。为了推动转型和取得结果,公司开始关注低代码解决方案,作为一个强大、精简的方法来让开发者完成工作。
云云众生s
2024/03/27
1580
使用低代码开发平台进行应用程序开发有哪些优势?
低代码开发平台是指不用编码或少量编码即可快速产生应用程序的开发平台。依据平台的可视化开发方式,具有不同经验水准的开发者能通过图型操作面板、系统软件拖拽组件和模型驱动思维创建网页和移动应用程序。低代码开发平台在如今企业数字化的蓬勃发展中发挥着不可替代的功效。那么使用低代码开发平台开发应用程序的具体优点是什么呢?如何选择合适的低代码开发平台?
Zoho Creator低代码
2024/04/16
2490
为企业应用开发提速,写给企业IT部门的低代码开发基础知识
简介:应用程序开发长期以来一直是IT部门和业务部门面临的问题。 IT部门总是被新的应用程序需求弄得不堪重负。他们不可能完成业务部门想要完成的每一个项目。 同时,业务部门的用户厌倦了等待,并开始完全绕过IT部门。 今天,我们来探索一下“低代码开发”这个概念,并阐述它将如何帮助解决这个问题,为企业应用开发提速。
葡萄城控件
2019/11/21
7660
超火的低代码平台长什么样
随着数字化转型的不断推进,低代码平台也在高速发展中。越来越多的企业开始慢慢习惯于低代码平台的优势,并从中获益。低代码平台厂商也在不断推陈出新,以跟紧市场变化,简化开发者的工作。曾有专家预计,随着低代码与商业的紧密结合,到 2030 年,使用低代码构建的应用程序将产生价值 1870 亿美元的收益。
码匠Majiang
2022/11/08
5940
超火的低代码平台长什么样
2024国内外低代码开发平台排名,二十大主流低代码开发平台
IT圈的风口总是一轮又一轮,但是只有当浪潮退去,还能幸存的,才能是真正具备社会价值的产品。把这个规则放在——“低代码”这个赛道上,也是一样。
informat低代码
2024/06/06
3K0
2024国内外低代码开发平台排名,二十大主流低代码开发平台
2023年受人欢迎的低代码开发平台大盘点
随着企业对于降低成本和加快软件开发的需求增加,低代码开发平台逐渐成为一种受欢迎的选择。这些平台提供了拖放界面和预置组件,使得开发人员可以用更少的代码创建复杂的应用软件。低代码开发平台不仅有助于企业加速数字化转型,而且还能打破业务部门和IT部门之间的沟通障碍,让业务需求能够更快地得到实现。
informat低代码
2023/08/01
8360
18个顶级开源低代码开发平台
低代码/无代码开发平台使用图形向导构建软件,而不是像传统方法那样使用计算机编程语言来构建应用程序。在本文中,我们介绍面向个人和企业用户的18个开源的最佳低代码/无代码平台。
用户5687508
2021/07/02
13.8K1
低代码开发平台:颠覆还是辅助?
本号已有原创文章200+篇,以DevOps为基石,洞察研发效能全貌,涵盖从需求管理到运营监控的完整流程。无论您是项目经理、产品经理、开发人员、测试人员,还是运维人员,在这里您都可以有所收获,同时深入理解其他角色的工作内容,共同助力DevOps的成功落地。欢迎关注,有任何问题可发送私信~
DevOps持续交付
2024/01/07
2290
低代码开发平台:颠覆还是辅助?
选择低代码开发平台需要注意哪些事项?
低代码开发平台和零代码开发平台是近几年时兴的一种新的程序开发方法。该模式的特征是可以使用用户界面、拖拽操作等方式快速构建应用软件软件,从而减少开发者的学习标准,使每个人都能变成开发者。
Zoho Creator低代码
2024/04/19
1890
免费的低代码开发平台有哪些?
近年来,低代码行业逐渐成为了人们口中的“香馍馍”,尤其是在中、美地区,几乎每周都有一家低代码/无代码平台(No-Code)的公司融资。而根据Gartner的报告显示,低代码/无代码的市场,仅在去年(2021年)就增长了25%,分析软件市场也在一年内成长了20%。放眼国内市场,也是发展得如火如荼。
informat低代码
2022/07/25
12.2K1
免费的低代码开发平台有哪些?
低代码开发平台的兴起:机遇与挑战
随着数字化转型的加速,企业对于快速构建和部署应用程序的需求日益增长。"低代码"开发平台应运而生,它们承诺让非专业人士也能快速构建应用程序。这种技术的出现,无疑给IT行业带来了一场革命。本文将从技术概览、效率与质量的权衡、挑战与机遇三个方向,深入探讨低代码开发平台的兴起。
正在走向自律
2024/12/18
2440
低代码开发平台的兴起:机遇与挑战
低代码开发平台
总结: Visual LANSA 将使专业开发人员可以比传统编码更快地创建应用程序,并且其控制量比在低代码平台中通常看到的要高得多。
ruochen
2021/11/24
2.4K0
低代码开发平台有哪些比较好用的?
“低代码开发平台”算是近几年软件开发领域中的一个热点,所以在行业发展的过程中,不乏有很多软件开发商与无代码平台都来蹭“低代码”的概念,但实际上用过的人都清楚明白,低代码与无代码并不是完全相同的概念,二者之间的能力和解决的业务场景都不一样。无代码平台主要是解决轻量级应用的开发,而低代码开发平台则更适合于复杂业务应用的开发。
informat低代码
2023/04/11
1.8K0
低代码开发平台有哪些比较好用的?
今天来简单地聊一聊低代码/零代码工具
低代码甚至是零代码工具一直被人非常看好和追捧,尤其是在云技术各种催生的背景之下,人们总是期望有一种工具或平台能够快速实现应用开发和部署,不写代码或者写少量代码就能够实现客户的需求,对软件厂商或云厂商来说都是省心省力的事情。
SAP斯凯普斯
2021/05/21
7930
今天来简单地聊一聊低代码/零代码工具
盘点:2022年国内比较主流的低代码开发平台有哪些?
史上最全“低代码开发平台”介绍合集,2022年国内30家优秀低代码开发平台汇总盘点!这些平台必须拥有姓名!(以下排名不分先后)
informat低代码
2022/06/07
5.1K0
推荐阅读
相关推荐
【如何看待“低代码”开发平台的兴起?】效率提升还是质量隐忧?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档