理解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开发工具包命令,我们现在想让在所 ...
随机推荐
- ajax和jsonp
ajax和jsonp 前言:ajax和jsonp可以与后台通信,获取数据和信息,但是又不用刷新整个页面,实现页面的局部刷新. 一.ajax 定义:一种发送http请求与后台进行异步通讯的技术. 原理: ...
- JS开发打气球游戏
JS开发打气球游戏 观视频<月薪4万的程序员有多强?半小时原生JS开发打气球游戏,征服现场数万人!> 清晨,日常打开B站,被首页此视频的标题所吸引,虽一看就是标题党,但还是没能抑制住好奇心 ...
- js导出带格式的表格(包括单元格合并,字体样式等)
function HtmlExportToExcelForEntire() { var uri = 'data:application/vnd.ms-excel;base64,', template ...
- java okhttp 发送图片
@RequestMapping(value="/demo2", method=RequestMethod.POST) @ResponseBody public String dem ...
- Redis高可用之集群配置(六)
0.Redis目录结构 1)Redis介绍及部署在CentOS7上(一) 2)Redis指令与数据结构(二) 3)Redis客户端连接以及持久化数据(三) 4)Redis高可用之主从复制实践(四) 5 ...
- BZOJ1170 : [Balkan2007]Cipher
首先对于每个位置,求出它开始长度为y的横行的hash值,然后对于hash值再求一次竖列的hash值,排序后求出众数即可. 时间复杂度$O(n^2\log n)$. #include<cstdio ...
- Ural 2037. Richness of binary words 打表找规律 构造
2037. Richness of binary words 题目连接: http://acm.timus.ru/problem.aspx?space=1&num=2037 Descripti ...
- Linux学习笔记02—磁盘分区
下面介绍四种最常见的分区方式: (1) 最简单的分区方案. SWAP分区:即交换分区,建议大小是物理内存的1-2倍. /分区:建议大小在6GB以上. 使用以上的分区方案,所有的数据都在/分区上, ...
- Linux下的sqlserver简单试用
微软自2017年就推出了可以在linux上使用的sql-server,最近接触到了一个用sqlserver的项目,便尝试使用了一下. 下载 为了简化安装,我还是使用的docker的方式,镜像可以直接从 ...
- Using an LPC-Link2 as an LPC4370 evaluation board
https://www.lpcware.com/content/faq/lpcxpresso/using-lpclink2-as-lpc4370-eval As well as being a sta ...