`紧接着续篇:探讨ThreadLocal(一)
- Q1:ThreadLocal和Thread相互关系究竟如何?
- Q2: 为何每启动一个线程便会启用一次
initialValue()
呢?
一、实例回望
1.1 自定MyThreadLocal
在回答Q1前,我们首先依据续篇中也了解的ThreadLocal来效仿写一个MyThreadLocal。
public class MyThreadLocal<T>{
private T t;
protected T initialValue(){return null;}
public T get() {
//如果没有值,则启用initialValue复位一个值
if (t == null){
return initialValue();
}
return t;
}
}
随后修改一下ThreadId类,改成刚所写的MyThreadLocal去管理进程Id。
public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(0);
private static final MyThreadLocal<Integer> threadId = new MyThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
public static Integer get() {
return threadId.get();
}
public static void main(String[] args) {
new Thread(() -> System.out.println(ThreadId.get())).start();
new Thread(() -> System.out.println(ThreadId.get())).start();
new Thread(() -> System.out.println(ThreadId.get())).start();
new Thread(() -> System.out.println(ThreadId.get())).start();
}
}
看运作结论,发觉跟用ThreadLocal一毛一样。
什么原因,我就用短短的两行编码就实现了ThreadLocal的功效了?其实也不是,大家如下所示修改一下测试程序,MyThreadLocal立刻现出原形。
1.2 根本原因
测试程序就不贴了,立即比照看结果。
1.3 剖析源代码
这时候禁不住更为好奇心了,ThreadLocal如何做到管理方法不一样进程id的?咱们融合get()
源代码来寻找一下
这儿我们可以看到冒出2个新的东西 ThreadLocalMap 和 ThreadLocalMap.Entry。这俩实际具体内容先无论,可是对比Map和Map.Entry之间的关系。这俩估计也大差不差。接着看get()
跟大家分享个小窍门:首先看代码注释,前去追源代码才会事半功倍。上图中创作者告诫我们:用这种方法回到现阶段进程的thread-local
自变量数值。如果这些自变量并没有值,这样就会启用initialValue()
回到一个初值。换句话说每一个进程都有一个叫thread-local
的自变量咯?咱们就确认一下Thread中是否存在这一自变量。发现这个同样存在,但它并不是ThreadLocal,反而是ThreadLocalMap。
那快来看看ThreadLocalMap和ThreadLocal又是如何个事吧。首先看关系网,ThreadLocal含有个内部类ThreadLocalMap,ThreadLocalMap中还套了个Entry类。
再看源码:
我勒个去,Entry继承WeakReference
插口,泛型也是ThreadLocal。此后大家梳理一下Thread和ThreadLocal之间的关系。
1.4 汇总
1.4.1 ThreadLocal和Thread相互关系
从他们讲解的数据看,ThreadLocal和Thread中间没有直接关联。非要说,有的话还可以。由于ThreadLocal是ThreadLocalMap的key。
1.4.2 为何每启动一个线程便会启用一次initialValue()
呢?
其实大家汇总穿上图的关联后,正在看这种情况的时候会如梦初醒。而且每个进程都有各自的ThreadLocalMap
去存放value。每一个进程在启用ThreadId.get()
时,都因为自已的ThreadLocalMap
里没有value,而启用initialValue()
.因而虽然不同类型的进程共用一个ThreadLocal
做为key,但由于ThreadLocalMap
不一样,二者之间互不干涉。
最终我了解了原先ThreadLocal不判空,我们可以通过它所提供的get()
实际是提的Entry的value,它他也是做为key存有Map里的。那时候使用上我们不需要关注ThreadLocal的结构完成小细节,如果通过它所提供的set()
/get()
就可实际操作进程本地变量。是否特别像实体类的感觉了。封装形式内部实际完成,给予接口调用。
续篇大家然后讨论:为何ThreadLocal能解决线程安全问题?Threadlocal为什么使用弱引用?ThreadLocal有什么适用场景,要注意什么难题?
本站是一个以CSS、JavaScript、Vue、HTML为中心的前端开发技术网址。我们的使命是为众多前端工程师者提供全方位、全方位、好用的前端工程师专业知识和技术服务。 在网站上,大家可以学到最新前端开发技术,掌握前端工程师最新发布的趋势和良好实践。大家提供大量实例教程和实例,让大家可以快速上手前端工程师的关键技术和程序。 本站还提供了一系列好用的工具软件,帮助你更高效地开展前端工程师工作中。公司提供的一种手段和软件都要经过精心策划和改进,能够帮助你节约时间精力,提高研发效率。 此外,本站还拥有一个有活力的小区,你可以在社区里与其它前端工程师者沟通交流技术性、交流经验、处理问题。我们坚信,街道的能量能够帮助你能够更好地进步与成长。 在网站上,大家可以寻找你需要的一切前端工程师网络资源,使您成为一名更加出色的网页开发者。欢迎你添加我们的大家庭,一起探索前端工程师的无限潜能!