由于上次主要分析的是如何解决异步获取不到Session的问题,分析留下的思考问题就不展开了:使用InheritableThreadLocal来传递Session,为什么用线程池不一定能拿到Session,而不是拿不到呢?在Java中,一个Java线程就是一个操作系统线程。创建线程需要通过newThread来创建,JVM将操作系统线程绑定到Thread。即使使用线程池,也需要通过newThread创建线程。Thread类有两个ThreadLocal字段:publicclassThreadimplementsRunnable{ThreadLocal.ThreadLocalMapthreadLocals=null;ThreadLocal.ThreadLocalMapinheritableThreadLocals=null;}InheritableThreadLocal是ThreadLocal的子类,本质上是一个ThreadLocal。在Thread类中,threadLocals和inheritableThreadLocals都是线程对象私有的,只能通过当前线程对象写入和获取数据,但是Thread会把写入inheritableThreadLocals的数据传递给子线程的inheritableThreadLocals。当我们向ThreadLocal或InheritableThreadLocal写入数据时,写入过程如下:1.ThreadLocal或InheritableThreadLocal首先调用Thread#currentThread静态方法获取当前线程的Thread对象;2、获取Thread对象的threadLocals或inheritableThreadLocals;InheritableThreadLocal对象作为键值写入当前Thread对象的threadLocals或inheritableThreadLocals字段。所以Thread的threadLocals和inheritableThreadLocals的key是ThreadLocal或者InheritableThreadLocal的实例,value是写入的数据。threadLocals我在之前的文章《反向理解ThreadLocal,或许这样更容易理解》已经详细介绍过了。本文重点介绍如何将inheritableThreadLocals传递给子线程。默认情况下,当我们使用newThread()创建线程时,会在Thread构造方法中通过Thread#currentThread获取到当前线程,并将当前线程作为新创建线程的父线程,所以有是父子线程关系。无论使用哪种重载的构造方法来创建Thread,构造方法中都会调用init方法完成初始化并对Thread字段赋值,而init方法中有这么一段代码:privatevoidinit(ThreadGroupg,Runnabletarget,Stringname,longstackSize,AccessControlContextacc,booleaninheritThreadLocals){......如果(inheritThreadLocals&&parent.inheritableThreadLocals!=null)this.inheritableThreadLocals=ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);......}在init方法,由于inheritThreadLocals参数默认为true,所以只要父线程的inheritableThreadLocals字段不为空,将父线程的inheritableThreadLocals复制到当前创建的线程对象中,就实现了数据的传递保存在父线程的inheritableThreadLocals中给子线程。使用InheritableThreadLocal我们要考虑的问题:内存泄漏。ThreadLocal.ThreadLocalMap使用数组来存储元素。与HashMap不同的是,它使用开放的寻址方式来解决哈希冲突。没有链表,数组可以通过动态扩充数组来无限存储元素。数组元素的类型是Entry。当我们向ThreadLocal.ThreadLocalMap写入一个key-value时,ThreadLocalMap会将key和value打包成一个Entry,通过key的hashcode值计算出索引值,并将Entry放入一个数组中。ThreadLocal.ThreadLocalMap.Entry类的源码如下:staticclassEntryextendsWeakReference
