java私有变量 线程_Thread是如何将变量线程私有的

ThreadLocal与ThreadLocalMap与Thread

ThreadLocal是一个线程本地变量的引用,它就是一个普通的对象,只是它的内部实现比较特别。为什么叫这个名字,这与其实现有关。

ThreadLocalMap是ThreadLocal的静态内部类,同时是一个对象池的角色,看其命名以关键词”Map“结尾也可以知道,其持有多个ThreadLocal对象的”映射引用“,具体后面再讲。

Thread代表的就是一个Java中的线程对象,在编码、运行时代表的就是一个线程。对线程的操作可以通过一个Thread对象进行,Thread持有一个ThreadLocal.ThreadLocalMap类型的变量,通过此操作让Thread持有了ThreadLocalMap对象的引用。

三者的关系如下图,Thread内部持有一个ThreadLocalMap,ThreadLocalMap中保存有当前线程所有的的ThreadLocal对象。

e868d4e372742a0ca9d697588c9c6402.png

透过现象看本质

使用ThreadLocal的时候,最常用的就是三个方法set(),get(),remove(),而且使用的顺序一般也是这三步。 看上面的图,Thread持有ThreadLocalMap,ThreadLocalMap保存了ThreadLocal,怎么通过ThreadLocal#set()方法就将值保存到了线程私有的ThreadLocalMap中去了?而且使用的时候直接ThreadLocal#get()方法就能拿到值?

set()方法

ThreadLocal#set()的源码如下:

public void set(T value) {

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

if (map != null)

map.set(this, value);

else

createMap(t, value);

}

在set方法中直接通过Thread的静态方法currentThread()获得了当前的Thread对象引用。currentThread()方法被native修饰,具体实现不在此探究。

然后使用getMap()方法,传入了当前的Thread对象引用,获得了ThreadLocalMap对象。getMap()方法的源码如下,其实现更简单,直接返回了Thread对象所持有的threadLocalMap对象。

ThreadLocalMap getMap(Thread t) {

return t.threadLocals;

}

获得ThreadLocalMap对象后,先判断其是否为空,若为空则创建一个新的ThreadLocalMap对象出来,并将当前ThreadLocal的set()方法传入的对象,放入到新创建的ThreadLocalMap中;若已经存在,则直接将并将当前ThreadLocal的set()方法传入的对象,放入到ThreadLocalMap中。

这里为什么会有一个判空的操作?

在每一个Thread内部都维护有一个ThreadLocakMap对象,用于保存当前线程独占的变量(ThreadLocal)与其值。ThreadLocalMap在默认情况下是一个空对象,在第一次向ThreadLocal对象中set值的时候才会初始化,这样设计的原因是:ThreadLocal是一个低频功能,绝大部分情况下是不需要使用的,这样设计可以节省内存空间。

createMap(t, value)方法

createMap方法,从其方法的命名就能看出是用来创建Map映射对象的,这里创建的就是ThreadLocalMap。 继续探究createMap(t, value)方法,向下一路追踪代码而去,会找到ThreadLocalMap对象创建的代码如下

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的构造函数有两个参数,一个是ThreadLocal>类型的firstKey,一个是Object类型的firstValue。从这两个变量的命名来看,一个Key、一个Value,符合Map映射的K-V形式。

从代码的逻辑实现上可以看出来,ThreadLocalMap实际持有的是一个Entry类型的数组,初始容量INITIAL_CAPACITY=16。ThreadLocalMap的本质就是一个Entry数组,Entry数组中的元素持有的就是ThreadLocal变量的值。这整个Entry数组就是一个线程独占的ThreadLocalMap。既然是一个数组,为什么命名的时候要叫一个XXXMap呢?

为什么会叫“ThreadLocakMap“这个以Map关键字结尾的变量名?

或者:ThreadLocalMap为什么被命名为一个Map,映射是谁映射的谁?

从宏观来看,ThreadLocalMap维护的就是一个以ThreadLocal变量为Key,以具体值为Value的映射关系对象。

ThreadLocakMap是对Entry数组的封装,封装后的表现就是以Entry数组的下标映射到的具体的值的行为,所以以关键词”Map“作为变量名的后缀。既然叫“Map”,则可以看出其设计初衷为实现一个“key-Value”形式的存储。在实际的实现中,Value好说,就是ThreadLocal中set进去的值对象。那么Key呢?ThreadLocalMap的本质是一个Entry数组,所以可以看出此处Key的本质就是Entry数组的下标,对应Java中就是一个int类型的值。但在实际的使用中不可能直接指定这个“下标”的值,所以在ThreadLocalMap中已经封装了这个“下标”的生成方法。该生成方法的启发因子就是ThreadLocal对象的hashCode,从宏观来看,ThreadLocalMap维护的就是一个以ThreadLocal变量为Key,以具体值为Value的映射关系对象。所以,在实现的编码使用中,直接使用ThreadLocal的get方法就能取到之前set进去的值。

get()方法

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();

}

与set()方法一样,在set方法中直接通过Thread的静态方法currentThread()获得了当前的Thread对象引用。

与set()方法一样,使用getMap()方法,传入了当前的Thread对象引用,获得了ThreadLocalMap对象。

判断得到的ThreadLocalMap(即当前线程的ThreadLocalMap)是否为null,如果是null,则直接使用setInitialValue()返回给当前的ThreadLocal变量初始化一个默认的值并放入当前线程的ThreadLocalMap中,并将默认值返回给调用方。这个默认值就是一个“null”。

如果当前线程的ThreadLocalMap不是空的,则使用getEntry方法,通过当前的ThreadLocal变量(即ThreadLocalMap中的Key)获得对应的值(即ThreadLocalMap中的Value)。 若拿到的值不是null则进行一次类型转换,最后返回给调用方。如果拿到的值是null,则初始化一个默认的“null”返回给调用方。

一个ThreadLocal到底可以持有几个对象的引用

由此可以看出,在一个线程中可以持有多个ThreadLocal对象,但是每个ThreadLocal对象只能持有一个变量值。线程所持有的所有的ThreadLocal及其值都保存在Thread私有的ThreadLocalMap映射对象中。

现在再看这个问题,感觉自己挺傻的。😆😆😆😆😆这确实是当年我脑海中蹦出来的问题。也不知道当时脑子在想啥,就是没转过这个弯儿来~~~

为什么直接通过ThreadLocal就能操作Thread的线程私有对象映射迟ThreadLocalMap呢?

通过以上的分析,ThreadLocal、ThreadLocalMap、Thread三者之间的关系,可以更新为下图:

1c0a950d8b5eb50e47c292a8f13d4608.png 在ThreadLocal的set与get方法中通过Thread的静态本地方法currentThread获得当前的Thread对象引用,从而操作Thread持有的ThreadLocalMap对象池。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值