ThreadLocal即线程局部变量的意思!所以什么是线程局部变量?这玩意有什么鸟用?是不是面试被问到了说不出个一二三?今天就来扒一扒这货的源码,从根本上了解这货是干啥的。

Thread ,ThreadLocalMap,Entry三者关系

其实研究下来他的源码实现,其实也没想象的那么复杂,其最主要有以下几点:

1、Java可以通过Thread.currentThread()来获得当前的Thread的实例对象。既然能拿到这Thread对象实例,那么我们就可以操作该实例(的属性),比如为该Thread对象设置一个值什么的。
2、每一个Thread对象都有一个ThradLocalMap实例,该实例有一个一个Entry组成的数组,Entry对象有两个主要属性:value和ThreadLocal的弱引用,其中value这个属性就是值设置给当前线程所持有,也是ThreadLocal的核心属性:

static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}
}

注意Entry继承自WeakReference,其key就是ThreadLocal对象!(图1)

结合1和2两个知识点,我们就可以知道我们拿到Thread对象之后,就可以操控当前线程对象的ThreadLocalMap对象,然后把想要保存的value交给ThreadLocalMap的Entry的value属性,Thread,ThreadLocalMap,value三者之间的关系可以用下图表示(图2):


通过上图我们可以得出这么一个结论:一个Thread对象持有一个ThreadLocalMap对象,然后呢,一个ThreadLoalMap对象又包含了多个ThreadLlocal对象及ThreadLocal对象所在线程的value!!!一言以蔽之: 一个Thread对象可以持有多个ThreadLocal对象的变量值value

那么ThreadLocal和Thread又有啥关系呢?二者是怎能对value进行读取的呢?下面就根据源码来简单的分析下。

ThreadLocal和Thread的关联

先看看ThreadLocal的set方法:

 public void set(T value) {//获取当前线程Thread t = Thread.currentThread();//获取当前线程持有的ThreadLocalMapThreadLocal.ThreadLocalMap map = getMap(t);//将value设置给threadlocalMapif (map != null)map.set(this, value);elsecreateMap(t, value);}

set方法很逻辑很简单(j结合上图2看更好理解):
1、通过currentThread方法拿到当前Thread对象
2、获取当前Thread对象的ThreadLoalMap对象
3、将value连同ThreadLocal对象自己组成一个Entry对象保存在
ThreadLoalMap的Entry类型的数组中。

在来看看ThreadLocal的get方法:

public T get() {//获取当前线程Thread t = Thread.currentThread();//获取当前线程的ThreadLocalMap对象ThreadLocalMap map = getMap(t);if (map != null) {//获取与ThreadLocal对象想关联的valueThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")//获取值T result = (T)e.value;return result;}}//为空返回初始化值return setInitialValue();
}

可以发展get的整体逻辑也很简单:
1、获取当前Thread对象
2、获取当前Thread对象的ThreadLocalMap对象
3、从ThreadLocalMap中获取与ThreadLocal相关联的Entry对象,具体的就是以ThreadLocal为key获取。
4、获取步骤3的Entry的value属性,并返回之。

通过整体观察get和set方法可以得出如下结论:ThreadLocal对象调用set方法就是往Thread对象的ThreadLocalMap里面添加值;ThreadLocal对象调用get方法就是从Thread对象的ThreadLocalMap里面获取值。核心就是操纵Thread对象的ThreadLocalMap对象进行value的读和写。原理就这么简单。

那么位于不同线程的不同ThreadLocal对象,在其他线程里保存值是一个什么样的关系呢?可以通过下图来清晰的描述出来:


通过上图可以知道我们在线程X里面定义了一个threadLocalX的变量,然后线程A调用threadLocalX的set方法将x1这个值设置给了线程A,线程B同样调用threadLocalX的set方法将x2这个值这是给了线程B.当我们操纵threadLocalX的get方法来取值的时候,此时get方法运行在哪个线程,取得就是哪个线程保存的值,比如如果此时A线程在执行,那么拿到的值就是x1.

ThreadLocal的使用实例

通过博主的Android的消息机制详解这篇博文中,我们在知道在Android中一个线程只有一个Looper对象,那么是怎么做到的呢?就是ThreadLocal发挥了作用,看看Looper的prepare方法:

//定义一个静态的ThreadLocal变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static void prepare(boolean quitAllowed) {//一个Thread只能关联一个Looper对象if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));}

观察prepare方法可以知道,先通过sThreadLocal的get方法判断当前线程是否已经拥有了一个Looper对象,如果有就抛出一个异常;如果当前线程还没有设置Looper对象,则调用ThreadLocal的set方法,初始化一个Looper对象交给当前线程:sThreadLocal.set(new Looper(quitAllowed));
这样就确保了一个线程只有一个Looper对象。

线程局部变量到此也就好理解了,其实就是局部于线程对象的变量。在Java中Thread也是一个对象,平时开发中我们自定义一个对象也会持有一个List或者Map集合来持有一些数据,比如:

class MyObject{List<String> values;//orMap<String> maps;
}

而Thread只不过是用了ThreadLocalMap而已:

class Thread implements Runnable {ThreadLocal.ThreadLocalMap threadLocals = null}

到此为止,关于ThreadLocal的原理已经基本分析完毕,至于内部是怎么set和get的,敬请期待后续博文。

参考资料:
ThreadLocal源码分析

ThreadLocal原理浅析相关推荐

  1. Seata 分布式事务的使用和原理浅析

    Seata 分布式事务的精简使用教程和原理浅析 一.说明 二.Seata 简介 2.1.Seata 是什么? 2.2.Seata 的整体架构 2.2.1.主要角色 2.2.2.整体架构和工作流程图 2 ...

  2. Python标准库queue模块原理浅析

    Python标准库queue模块原理浅析 本文环境python3.5.2 queue模块的实现思路 作为一个线程安全的队列模块,该模块提供了线程安全的一个队列,该队列底层的实现基于Python线程th ...

  3. Python标准库threading模块Condition原理浅析

    Python标准库threading模块Condition原理浅析 本文环境python3.5.2 threading模块Condition的实现思路 在Python的多线程实现过程中,在Linux平 ...

  4. 第一篇: 词向量之Word2vector原理浅析

    第一篇: 词向量之Word2vector原理浅析 作者 Aroundtheworld 2016.11.05 18:50 字数 1353 阅读 5361评论 1喜欢 9 一.概述 本文主要是从deep ...

  5. SPI及其工作原理浅析

    说明.文章摘自:SPI协议及其工作原理浅析 http://bbs.chinaunix.net/thread-1916003-1-1.html 一.概述. SPI, Serial Perripheral ...

  6. .NET1.1中预编译ASP.NET页面实现原理浅析[1]自动预编译机制浅析

    .NET1.1中预编译ASP.NET页面实现原理浅析[1]自动预编译机制浅析 .NET1.1中预编译ASP.NET页面实现原理浅析[1]自动预编译机制浅析 作者:&;nbsp来自:网络 htt ...

  7. LinkedList 的实现原理浅析

    转载自 LinkedList 的实现原理浅析 LinkedList内部结构 查看LinkedList的源码,发现其继承自AbstractSequentialList,实现了List,Deque,Clo ...

  8. Spring AOP原理浅析及入门实例

    上篇问题及Spring AOP实现原理浅析 上篇说了一个AOP编程问题,那是一个错误的AOP案例.它的错误在A类中,再次粘贴A类代码: @Componentpublic class AImpl imp ...

  9. ThreadLocal原理解析以及是否需要调用remove方法

    平常的开发过程中,如果有个类不是线程安全的,比如SimpleDateFormat,要使这个类在并发的过程中是线程安全的,那么可以将变量设置位局部变量,不过存在的问题就是频繁的创建对象,对性能和资源会有 ...

  10. redis单线程原理___Redis为何那么快-----底层原理浅析

    redis单线程原理 redis单线程问题 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程. 1. 为什么说redis能够快速执行 ...

最新文章

  1. 最小生成树之克鲁斯卡尔(Kruskal)算法
  2. Qt C++模版函数示例
  3. mount:在/dev/sr0上找不到媒体
  4. 数据结构实验之排序一:一趟快排
  5. ming window 交叉编译_opencv3编译pc端及交叉编译arm端
  6. Python 用pygame模块播放MP3
  7. JavaScript(三)——对象与数组基础及API
  8. DedeCMS 批量删除垃圾注册用户和垃圾文档
  9. Express中间件(非常详细)
  10. keras从dataframe中读取数据并进行数据增强进行训练(分类+分割)
  11. php环境下cache失效,cache缓存失效高并发读数据库的问题
  12. React.memo 是个啥么鬼?有什么用???
  13. 《Redis开发与运维》学习第五章
  14. Fisher精确检验【转载】
  15. Win11电脑摄像头打开看不见,显示黑屏如何解决?
  16. 微软 MSCRM 教育成功案例 界面展示
  17. 在linux开发板上显示图片,制作开发板的logo标签
  18. PCB板上的蓝宝石---关于光学定位点的DFM
  19. 将OPenCV的Dll 动态连接库改名字,变成自己想要的动态连接库名字
  20. 做为一名大数据新手,如何成为大数据工程师?附上学习路线

热门文章

  1. 牛b硬件信息修改大师_比X大师更靠谱?一款真正良心的硬件检测工具
  2. python定义私有变量的方法_Python中私有属性的定义方式
  3. 鼠标在滑块上滚轮控制_直线导轨(滚轮导轨)与线轨(滚珠导轨)的优劣势对比...
  4. cedit多行文本设置透明背景会重叠_python:电商用户评价文本分析(wordcloud+jieba)...
  5. 计算机速录学什么,学速录需要哪些能力
  6. Rviz教程系列第一章之Markers
  7. C#图像处理初学之平移和镜像
  8. 将空闲空间合并到现有分区时出现错误: 检测到下列文件系统错误,分区容量未做调整, 无效的的文件记录
  9. NDCG、AUC介绍
  10. LeetCode之最大连续1的个数