一.简介及作用
在一些场景中,我们希望一些变量只能被某个线程访问,对其他线程是隔离的,就需要用到ThreadLocal.
ThreadLocal具有线程隔离性,用于解决多线程中相同变量的访问冲突问题.使每个线程读到的变量都是相独立的.线程内部变量仅相对于本线程而言可见.
二.简单使用
存储值,直接调用set()方法
取值,直接调用get()方法.
ThreadLocal<String> threadLocal = new ThreadLocal<>();
for (int i = 0; i < 5; i++) {
String threadName = "Thread" + i;
new Thread(() -> {
threadLocal.set(threadName);
System.out.println(threadName + "获取到值:" + threadLocal.get());
threadLocal.remove();
}, threadName).start();
}
输出结果:
注意:针对同一个Thread,ThreadLocal 只能存储一个值,如下案例:
三.原理:
-
首先Thread有一个成员变量ThreadLocal.ThreadLocalMap,也就是每一个Thread都自身绑定了一个ThreadLocal.ThreadLocalMap.
-
而ThreadLocal的静态内部类ThreadLocalMap,最主要的结构就是一个entry数组.
-
在存储元素的时候,首先我们能根据当前thread获取到当前绑定的ThreadLocalMap,然后利用threadlocalMap中的threadLocalHashcode()方法计算出当前要存储的entry数组中的下标位置,key就是threadLocal对象,value就是要存的值,组成一个entry存进去.
-
取元素的时候也是根据当前线程对象,拿到threadLocalMap,再根据this(threadLocal对象)计算hash值,找到下标,取值即可
四.自问自答
-
存储的数据结构是什么样的
thread.ThreadLocal.ThreadLocalMap–>[<threadLocal1,value1>,<threadLocal2,value2>]数组中的下标就是通过当前threadLocal对象hash得到的.
-
为什么要传一个数组呢?
对每一个线程来说,可能要存储的与之相关联的变量不只一个,所以必然需要一个集合去存储这些值. -
当发生Hash冲突的时候是怎样处理的?
ThreadLocal里面就有一个数组,不像hashMap,是数组+链表的结构,那么当ThreadLocal发生hash冲突的时候就会用到线性探测法,简单来说就是当冲突的时候,依次查找列表中离冲突单元最近的空闲单元,并且把新的键插入这个空闲单元. -
为什么要用线性探测法?
ThreadLocal 往往存放的数据量不会特别大(而且key 是弱引用又会被垃圾回收,及时让数据量更小),这个时候开放地址法简单的结构会显得更省空间,同时数组的查询效率也是非常高,加上第一点的保障,冲突概率也低开方地址法的缺点是容易产生堆积,不适用于大规模的存储.
链地址法:不存在堆积问题,冲突处理简单,适合大数据存储. -
为什么要是一个entry数组呢,不是一个单泛型得数组,比如说String数组?
其实终其原因,是因为其在hash冲突的时候使用了线性探测法,会依次往后寻找空位置去插入,
那么当我们去取值的时候,发生hash冲突,就得往后依次遍历比较哪一个是和我们存进去得key相等,才进行返回
今天的分享就到这里了,有问题可以在评论区留言,均会及时回复呀.
我是bling,未来不会太差,只要我们不要太懒就行, 咱们下期见.