InheritableThreadLocal异步传递数据实现原理

您所在的位置:网站首页 InheritableThreadLocal InheritableThreadLocal异步传递数据实现原理

InheritableThreadLocal异步传递数据实现原理

2023-10-09 10:10| 来源: 网络整理| 查看: 265

由于上次主要分析如何解决异步获取不到Session问题,所以没有展开分析留下的那个思考题:使用InheritableThreadLocal传递Session,为什么说使用线程池不一定能获取到Session,而不是一定获取不到?《替换Shiro框架后,上线就Bug了,异步线程获取不到Session》

在Java中,一个Java线程就是一个操作系统线程,创建一个线程需要通过new Thread创建,由JVM为Thread绑定操作系统线程,即便是使用线程池,也需要通过new Thread创建线程。

Thread类有两个ThreadLocal字段:

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

InheritableThreadLocal是ThreadLocal的子类,本质上就是一个ThreadLocal。

在Thread类中,threadLocals与inheritableThreadLocals都是线程对象私有的,只能通过当前线程对象写入和获取数据,只是Thread会将写入inheritableThreadLocals的数据传递给子线程的inheritableThreadLocals。

当我们往ThreadLocal或者InheritableThreadLocal写入数据时,写入过程为:

1、ThreadLocal或者InheritableThreadLocal先调用Thread#currentThread静态方法获取当前线程的Thread对象; 2、获取Thread对象的threadLocals或者inheritableThreadLocals; 3、将ThreadLocal或者InheritableThreadLocal对象作为key,将数据写入到当前Thread对象的threadLocals或者inheritableThreadLocals字段中。

因此,Thread的threadLocals与inheritableThreadLocals的key是ThreadLocal或者InheritableThreadLocal实例,value是写入的数据。关于threadLocals我在前面一篇《反向理解ThreadLocal,或许这样更容易理解》已经详细介绍过了,本篇重点分析inheritableThreadLocals是如何传递给子线程的。

默认情况下,当我们使用new Thread()创建一个线程时,在Thread的构造方法中会通过Thread#currentThread获取当前线程,将当前线程作为新创建线程的父线程,所以就有了父子线程关系。

无论使用哪个重载的构造方法创建Thread,都会在构造方法中调用init方法完成初始化为Thread字段赋值,而init方法中有这样一段代码:

private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { ...... if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); ...... }

在init方法中,由于inheritThreadLocals参数默认为true,所以只要父线程的inheritableThreadLocals字段不为空,就copy一份父线程的inheritableThreadLocals给当前创建的线程对象,这就实现了将父线程的inheritableThreadLocals存储的数据传递给子线程。

使用InheritableThreadLocal我们不得不考虑的问题:内存泄漏。

ThreadLocal.ThreadLocalMap使用数组存储元素,与HashMap不同,它通过开放定址法解决hash冲突,不存在链表,通过动态扩容数组可无限存储元素,数组元素的类型为Entry。

当我们往ThreadLocal.ThreadLocalMap写入一个key-value时,ThreadLocalMap把key和value包装成一个Entry,并通过key的hashcode值计算索引值,将Entry放到数组中。

ThreadLocal.ThreadLocalMap.Entry类的源码如下:

static class Entry extends WeakReference


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3