多个线程ThreadLocal中存的是什么
之前所学不精,现在看一下确实是,我ThreadLocal里如果都存的是一个共享变量的话,那么肯定是会两边都相同的。其实现在回头看这些代码就没有了当初学术不精时候的疑惑了,反正也被喷了,趁这个被喷的时间索性更正一下ThreadLocal的存储机制。
测试代码相当简单
public static void main(String[] args){ThreadLocal<String> tl1 = new ThreadLocal<>();tl1.set("tl1");System.out.println(tl1.get());}
这里要分析的也就两行
- ThreadLocal是怎么set的
- ThreadLocal是怎么get的
ThreadLocal是怎么set的
我们直接就看ThreadLocal的set方法
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}
这里面有四个点
- ThreadLocalMap类是个什么东西
- getMap方法是什么
- map.set方法是怎么set的
- createMap方法是什么,为什么需要线程参数 t
其实这里对于第四个点,我没点进去看也是有点疑惑的,为什么这个createMap方法的两个参数和上一句map.set的两个参数不一样,这不都是set一个键值对么,然后点进去就什么都知道了。(我之所以这么说是因为我觉得总会有人和我想的一样的)
下面逐一解释这4个点
ThreadLocalMap类是个什么东西:
ThreadLocalMap是ThreadLocal的一个静态内部类,内部指的看一下的东西如下
- Entry类,这个类比HashMap里的Entry简单多了,就一个构造,参数一个是Threadlocal键对象,一个Object值对象
- Entry数组table,做hash存储用的,懂HashMap的我就不说了
- 再就是阈值啊,初始大小之类的参数,这些在此文章就不关心了
getMap方法是什么:
getMap(Thread t)方法也是ThreadLocal类的一个内部方法
ThreadLocalMap getMap(Thread t) {return t.threadLocals;}
这方法的意思就是返回线程t的内部参数threadLocals,关于线程对象中threadLocals参数,总结起来就是你用不到ThreadLocal,线程对象的这个属性就一直是null,这一点了解到这里就可以了,有兴趣可以去看Thread类。接着上面的逻辑,如果getMap不是空,就用ThreadLocalMap的set方法置入一个以当前ThreadLocal对象为键,value为值得这么一个键值对;如果getMap为空,那么就以createMap方法set第一个值。
map.set方法是怎么set的:
ThreadLocalMap类的set方法
private void set(ThreadLocal key, Object value) {// We don't use a fast path as with get() because it is at// least as common to use set() to create new entries as// it is to replace existing ones, in which case, a fast// path would fail more often than not.Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal k = e.get();if (k == key) {e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();}
这里的处理逻辑几乎和HashMap的一样,虽然没HashMap那么细
- 计算当前键的hash值
- 去table里找,重复键就替换值,不重复就在该位置添加这个键值对
- 当前容量超过阈值就扩容然后rehash()
createMap方法是什么,为什么需要线程参数 t:
关于createMap方法的逻辑
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}
其中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);}
createMap方法的意思就是,构造一个新的ThreadLocalMap对象,将value对象塞进map,然后把传入的线程对象的threadLocals属性指向这个新ThreadLocalMap。
ThreadLocal是怎么get的
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null)return (T)e.value;}return setInitialValue();}
和HashMap一样,对应的key有就返回value,没有就null
到这里代码就讲解完了
总结(虽然我很想把总结写在开头)
- ThreadLocal进行set的时候,是在当前线程Thread中获取到有且唯一的ThreadLocalMap对象(如果没有就新建一个ThreadLocalMap对象设置进Thread的属性里),然后把自己作为键,value作为值set进这个Map里
- ThreadLocal进行get的时候,是从当前线程Thread中获取到有且唯一的ThreadLocalMap对象(Thread的ThreadLocalMap属性如果为空,也就是说这个线程从来都没有用过ThreadLocal设置过值,返回null),然后把自己做为键去该Map里面找,找到就返回对于的value,没有就返回null
昨天查资料看到了ThreadLocal这个类,原来一直没有仔细关注过,牛客网看到的一道题说
ThreadLocal用哈希表的形式为每一个线程都提供一个变量的副本
并且给的回答是正确的,这里我们想一下,什么叫变量的副本,如果某一个线程中副本被修改,那么,其他线程中“副本”会不会被修改。
我们来看以下代码:
public class Demo1 {private static ThreadLocal<Student> local = new ThreadLocal<Student>();public static void main(String[] args) {final Student student = new Student(); //所谓的副本原始对象,我们就存这个student.setAge(19); //给个初始值19/*** 实验策略是创建两个线程都进行保存student,然后都休息一段时间(给个3秒)* A线程休息完后修改student中的年龄为11* B线程在休息完3秒后继续休息2秒,目的是为了等A修改完* B线程休息完后取出自己所存的Student,看看里面的age到底是19还是11*/new Thread(){ //A线程public void run() {local.set(student); try {Thread.sleep(3000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}local.get().setAge(11);};}.start();new Thread(){ //B线程public void run() {local.set(student);try {Thread.sleep(5000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(local.get().getAge());};}.start();}
}
class Student{private int age;public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
最后结果为11,这个结果也就是说两个线程里面存的是同一个Student对象,修改时线程之间会被影响,而不是所谓的各自一个“副本”,谁也影响不了谁
具体ThreadLocal中是怎么存的,简单来说就是ThreadLocal类有方法调用当前Thread的ThreadMap对象(该对象不是HashMap的子类,但是同样实现了HashMap中的拉链式的结构,并且是Thread的内部类),拿到对象后把自己(ThreadLocal)当键,在里面找有没有已经存在的自己,也就是判断自己是否以前存过东西,存过就替换值,没存过就新开辟地方存值。
对于ThreadLocal的具体源码解析,博主http://blog.csdn.net/wanzaixiaoxinjiayou/article/details/49703135有具体分析。
多个线程ThreadLocal中存的是什么相关推荐
- Java Review - 线程池中使用ThreadLocal不当导致的内存泄漏案例源码分析
文章目录 概述 Why 内存泄露 ? 在线程池中使用ThreadLocal导致的内存泄漏 概述 ThreadLocal的基本使用我们就不赘述了,可以参考 每日一博 - ThreadLocal VS I ...
- WeakReference在ThreadLocal中的使用
1.ThreadLocal定义&作用 定义: ThreadLocal叫做线程本地变量,顾名思义,就是Thread的一个内部变量,这个ThreadLocal是属于某个线程的.就好比是某个人的老婆 ...
- ThreadLocal究竟存不存在内存泄漏?
写ThreadLocal原理的文章太多了,笔者这里不想再分析源码,也不想剖析其实现原理,其实也并不难,就直接说下ThreadLocal的原理吧. 1.ThreadLocal原理简介 假设定义了两个Th ...
- 【Java】强软弱虚四种引用,弱引用在ThreadLocal中的应用
Java中的引用类型 - 强软虚弱 1.强引用(StrongReference) Object strongReference = new Object(); 只要有引用指向它,就不会被回收.当内存空 ...
- java.线程池 线程数_如何在线程“ main”中修复异常java.lang.NoClassDefFoundError:Java中的org / slf4j / LoggerFactory...
java.线程池 线程数 此错误表示您的代码或您在应用程序中使用的任何外部库都在使用SLF4J库 (一个开放源代码日志记录库),但无法找到所需的JAR文件,例如slf4j-api-1.7.2.jar因 ...
- 如何在线程“ main”中修复异常java.lang.NoClassDefFoundError:Java中的org / slf4j / LoggerFactory...
此错误表示您的代码或您在应用程序中使用的任何外部库都在使用SLF4J库 (一个开放源代码日志记录库),但无法找到所需的JAR文件,例如slf4j-api-1.7.2.jar因此它是在线程" ...
- ThreadLocal中的3个大坑,内存泄露都是小儿科!
我在参加Code Review的时候不止一次听到有同学说:我写的这个上下文工具没问题,在线上跑了好久了.其实这种想法是有问题的,ThreadLocal写错难,但是用错就很容易,本文将会详细总结Thre ...
- java 父子线程 调用链_ZipKin原理学习--Zipkin多线程及线程池中追踪一致性问题解决...
在学习Zipkin分布式追踪系统中我们了解到Trace在整个调用链是一致的,在web服务中可以通过在header设置Trace值在不同的服务中进行传递,那样在一个服务内部不同的线程,甚至是线程池中Zi ...
- Java线程池线程突然没了_70%人答不全!线程池中的一个线程异常了会被怎么处理?...
#线程池中的一个线程异常了会被怎么处理? 估计很多人会是以下三点答案(me too): 1.抛异常出来并打印在控制台上 2.其他线程任务不受影响 3.异常线程会被回收 但是这里我先提前说一下以上三点不 ...
最新文章
- 整合营销系统推荐乐云seo_做seo优化前需要考虑哪些
- 我用 PyTorch 复现了 LeNet-5 神经网络(CIFAR10 数据集篇)!
- 公司行为(Corporate Actions)
- ffmpeg库音频解码示例
- okHttp源码解析------待续
- Python基础---循环、条件判断
- 【二分图】洛谷P2055假期的宿舍
- ESXI洗白安装黑群晖教程,附文件
- matlab风玫瑰图,基于Matlab的风玫瑰图绘制
- 权限管理系统,可以这么设计
- 升级Spring Boot 2.x后RelaxedPropertyResolver不可用的解决方案
- 机器学习中的数学——Nesterov Momentum
- 作为南方人,python教你怎么样看雪
- Java中的equals和==比较
- 如何快速搭建公司网站?
- HTML下拉菜单怎么做成横向,纯css实现横向下拉导航菜单(可做左侧类目导航)
- jsp mysql购物网站a_海来福家具商品交易购物网站的设计(JSP,MySQL)(附答辩记录)
- 有道云笔记、石墨笔记、 Effie …采编怎么选?
- Vue+SpringBoot+ElementUI实战学生管理系统-10.学生管理模块
- 准Z源逆变器模型预测控制Simulink仿真