理解Java中的ThreadLocal
提到ThreadLocal,有些Android或者Java程序员可能有所陌生,可能会提出种种问题,它是做什么的,是不是和线程有关,怎么使用呢?等等问题,本文将总结一下我对ThreadLocal的理解和认识,希望让大家理解ThreadLocal更加透彻一些。
ThreadLocal是什么
ThreadLocal是一个关于创建线程局部变量的类。
通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。
Global && Local
上面的两个修饰看似矛盾,实则不然。
- Global 意思是在当前线程中,任何一个点都可以访问到ThreadLocal的值。
- Local 意思是该线程的ThreadLocal只能被该线程访问,一般情况下其他线程访问不到。
用法简介
创建,支持泛型
1 |
|
set方法
1 |
|
get方法
1 |
|
完整的使用示例
1 |
|
ThreadLocal初始值
为ThreadLocal设置默认的get初始值,需要重写initialValue方法,下面是一段代码,我们将默认值修改成了线程的名字
1 |
|
Android中的应用
在Android中,Looper类就是利用了ThreadLocal的特性,保证每个线程只存在一个Looper对象。
1 |
|
如何实现
为了更好的掌握ThreadLocal,我认为了解其内部实现是很有必要的,这里我们以set方法从起始看一看ThreadLocal的实现原理。
下面是ThreadLocal的set方法,大致意思为
- 首先获取当前线程
- 利用当前线程作为句柄获取一个ThreadLocalMap的对象
- 如果上述ThreadLocalMap对象不为空,则设置值,否则创建这个ThreadLocalMap对象并设置值
源码如下
1 |
|
下面是一个利用Thread对象作为句柄获取ThreadLocalMap对象的代码
1 |
|
上面的代码获取的实际上是Thread对象的threadLocals变量,可参考下面代码
1 |
|
而如果一开始设置,即ThreadLocalMap对象未创建,则新建ThreadLocalMap对象,并设置初始值。
1 |
|
总结:实际上ThreadLocal的值是放入了当前线程的一个ThreadLocalMap实例中,所以只能在本线程中访问,其他线程无法访问。
对象存放在哪里
在Java中,栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。而堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。
问:那么是不是说ThreadLocal的实例以及其值存放在栈上呢?
其实不是,因为ThreadLocal实例实际上也是被其创建的类持有(更顶端应该是被线程持有)。而ThreadLocal的值其实也是被线程实例持有。
它们都是位于堆上,只是通过一些技巧将可见性修改成了线程可见。
关于堆和栈的比较,请参考Java中的堆和栈的区别
真的只能被一个线程访问么
既然上面提到了ThreadLocal只对当前线程可见,是不是说ThreadLocal的值只能被一个线程访问呢?
使用InheritableThreadLocal可以实现多个线程访问ThreadLocal的值。
如下,我们在主线程中创建一个InheritableThreadLocal的实例,然后在子线程中得到这个InheritableThreadLocal实例设置的值。
1 |
|
上面的代码输出的日志信息为
1 |
|
使用InheritableThreadLocal可以将某个线程的ThreadLocal值在其子线程创建时传递过去。因为在线程创建过程中,有相关的处理逻辑。
1 |
|
上面代码就是在线程创建的时候,复制父线程的inheritableThreadLocals的数据。
会导致内存泄露么
有网上讨论说ThreadLocal会导致内存泄露,原因如下
- 首先ThreadLocal实例被线程的ThreadLocalMap实例持有,也可以看成被线程持有。
- 如果应用使用了线程池,那么之前的线程实例处理完之后出于复用的目的依然存活
- 所以,ThreadLocal设定的值被持有,导致内存泄露。
上面的逻辑是清晰的,可是ThreadLocal并不会产生内存泄露,因为ThreadLocalMap在选择key的时候,并不是直接选择ThreadLocal实例,而是ThreadLocal实例的弱引用。
1 |
|
所以实际上从ThreadLocal设计角度来说是不会导致内存泄露的。关于弱引用,了解更多,请访问译文:理解Java中的弱引用
使用场景
- 实现单个线程单例以及单个线程上下文信息存储,比如交易id等
- 实现线程安全,非线程安全的对象使用ThreadLocal之后就会变得线程安全,因为每个线程都会有一个对应的实例
- 承载一些线程相关的数据,避免在方法中来回传递参数
注意:Android的ThreadLocal与Java实现略有不同,但是原理是一致的。
参考文章
- Java ThreadLocal
- Threadlocals and memory leaks in J2EE
- Java Thread Local – How to use and code sample
- ThreadLocal in Java – Example Program and Tutorial
from: http://droidyue.com/blog/2016/03/13/learning-threadlocal-in-java/index.html
理解Java中的ThreadLocal的更多相关文章
- 理解java中的ThreadLocal(转)
一.对ThreadLocal概述 JDK API 写道: 该类提供了线程局部 (thread-local) 变量.这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的 ...
- 理解java中的ThreadLocal 专题
ThreadLocal每一印象: public class IncrementWithStaticVariable{ private static int seqNum = 0; public int ...
- Java中的ThreadLocal深入理解
提到ThreadLocal,有些Android或者Java程序员可能有所陌生,可能会提出种种问题,它是做什么的,是不是和线程有关,怎么使用呢?等等问题,本文将总结一下我对ThreadLocal的理解和 ...
- 深入理解Java中的不可变对象
深入理解Java中的不可变对象 不可变对象想必大部分朋友都不陌生,大家在平时写代码的过程中100%会使用到不可变对象,比如最常见的String对象.包装器对象等,那么到底为何Java语言要这么设计,真 ...
- 理解Java中的弱引用(Weak Reference)
本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出, ...
- 深刻理解Java中final的作用(一):从final的作用剖析String被设计成不可变类的深层原因
声明:本博客为原创博客,未经同意,不得转载!小伙伴们假设是在别的地方看到的话,建议还是来csdn上看吧(原文链接为http://blog.csdn.net/bettarwang/article/det ...
- [译]线程生命周期-理解Java中的线程状态
线程生命周期-理解Java中的线程状态 在多线程编程环境下,理解线程生命周期和线程状态非常重要. 在上一篇教程中,我们已经学习了如何创建java线程:实现Runnable接口或者成为Thread的子类 ...
- 深入理解Java中的IO
深入理解Java中的IO 引言: 对程序语言的设计者来说,创建一个好的输入/输出(I/O)系统是一项艰难的任务 < Thinking in Java > 本文的目录视图如下: ...
- 深入理解Java中配置环境变量
深入理解Java中配置环境变量 配置的目的: 本来只在安装JDK的bin目下能运行java.exe,javac.exe,jar.exe,javadoc.exe等Java开发工具包命令,我们现在想让在所 ...
随机推荐
- bzoj [ZJOI2008]生日聚会Party
思路:dp, 用dp[ i ][ j ][ u ][ v ] 表示, 有n个人,其中有j个是男生,后缀区间中男生人数减去女生人数的最大值为u, 女生人数减去男生人数 的最大值为v, 然后就能写出状态转 ...
- spring boot 2.0.3+spring cloud (Finchley)6、配置中心Spring Cloud Config
https://www.cnblogs.com/cralor/p/9239976.html Spring Cloud Config 是用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持, ...
- linux 安装redis4.0
1.安装redis 第一步:下载redis安装包 wget http://download.redis.io/releases/redis-4.0.6.tar.gz 1 2 3 4 5 6 7 8 9 ...
- max 基础知识
MDX基本语法 MD询语句的结构及语法 MDX查询示例 基本的MDX SELECT语句包含一SELELCT字句和一个FROM字句,以及一个可选的WHERE子句.如下 SELECT {[Measures ...
- P2858 [USACO06FEB]奶牛零食Treats for the Cows
P2858 [USACO06FEB]奶牛零食Treats for the Cows区间dp,级像矩阵取数, f[i][i+l]=max(f[i+1][i+l]+a[i]*(m-l),f[i][i+l- ...
- 021.Zabbix的邮件告警-01
一 创建Media Administration---->Media types---->Create Media Type 选项 描述 Name 媒介名称,看着起名 Type 选择 ...
- jqplot导入包小结
对于jqplot画图的导入包,总结起来就是两种,一种是每个jsp文件都是导入一样的js或css包!这些包可以另新建一个文件存放,有如下这些包! <link rel="styleshee ...
- odoo国际化翻译
翻译功能简述 每个模块的翻译文件放在该模块目录下i18n目录里. 模块内相关字符串一般用英语写成,然后通过翻译模板导出功能,导出一个翻译模板po文件. 翻译人员使用翻译软件(poedit)进行翻译后, ...
- python 字典相关操作
字典 字典的增删改查 字典的创建方式: # 创建字典类型 info = { 'name':'李白', 'age':'25', 'sex':'男' } msg = { 'user01':'Longzel ...
- python语法(五)—函数
前面几天学习了python的基础语法,判断,循环,以及文件操作等等内容,对python也是有了一个认识.今天开始学习python的函数和模块. 函数 函数是什么?我的理解就是,他和java中的方法是一 ...