什么是ThreadLocal?顾名思义,ThreadLocal类可以理解为线程局部变量。也就是说如果定义了一个ThreadLocal,那么各个线程对这个ThreadLocal的读写都是线程隔离的,不会互相影响。它提供了一种通过传递可变数据来实现线程关闭的机制,每个线程都有自己独立的副本。在实际应用和实际开发中,我们真正使用ThreadLocal的场景还是比较少的,大部分都是在框架中使用。最常见的使用场景就是用它来解决数据库连接,Session管理等,保证每个线程使用的数据库连接是一样的。另外一个用的比较多的场景是解决SimpleDateFormat解决线程不安全的问题,不过现在java8提供了线程安全的DateTimeFormatter,有兴趣的同学可以去看看。它还可以用于优雅地传递参数。传递参数时,如果将父线程产生的变量或参数直接通过ThreadLocal传递给子线程参数,则参数会丢失。这个后面会介绍另外一个ThreadLocal来专门解决这个问题。ThreadLocalapi介绍ThreadLocal的API还是比较少的,先来看看这些api的使用,使用起来超级简单privatestaticThreadLocalthreadLocal=ThreadLocal.withInitial(()->"javafinance");publicstaticvoidmain(String[]args){System.out.println("获取初始值:"+threadLocal.get());threadLocal.set("注意:[java金融]");System.out.println("获取修改的最终值:“+threadLocal.get());threadLocal.remove();}输出结果:获取初始值:JavaFinance获取修改后的值:注意:【JavaFinance】炸鸡简单,几行代码就涵盖了所有API。我们先简单看一下这些API的源码。成员变量/**初始容量,必须是2的幂*Theinitialcapacity--MUSTbeapoweroftwo.*/privatestaticfinalintINITIAL_CAPACITY=16;/**表项,大小必须是2*Thetable的幂,resizedasnecessary.*table.lengthMUSTalwaysbeapoweroftwo.*/privateEntry[]table;/***Thenumberofentriesinthetable.*/privateintsize=0;/***Thenextsizevalueatwhichtoresize.*/privateintthreshold;//Defaultto0这里会有一个面试经常问到的问题:为什么entry数组的大小和初始容量不同一定是2的幂?对于firstKey.threadLocalHashCode&(INITIAL_CAPACITY-1);许多源代码使用hashCode&(-1)而不是hashCode%。这种写法的优点如下:用位运算代替取模,提高计算效率。为了使不同哈希值之间发生碰撞的概率更小,元素在哈希表中尽可能均匀地进行哈希。设置方法publicvoidset(Tvalue){Threadt=Thread.currentThread();ThreadLocalMapmap=getMap(t);if(map!=null)map.set(this,value);elsecreateMap(t,value);}设置方法是还是比较简单的,我们可以关注这个方法中的ThreadLocalMap。既然是map(注意不要和java.util.map混淆,这里指的是概念图),肯定有自己的key和value组成。我们根据源码可以看出,它的关键是可以简单的看做是ThreadLocal,但实际上ThreadLocal存储的是ThreadLocal的一个弱引用,它的值就是我们实际设置的staticclassEntryextendsWeakReference>{/**ThevalueassociatedwiththisThreadLocal.*/Objectvalue;//实际存储的值Entry(ThreadLocal>k,Objectv){super(k);value=v;}}Entry是ThreadLocalMap中定义的节点,它继承了WeakReference类,定义了一个Object类型的值,用于存储塞入ThreadLocal的值。我们来看看这个ThreadLocalMap位于哪里?我们看到ThreadLocalMap是一个位于Thread中的变量,我们的值是放在ThreadLocalMap中的,这样就可以实现各个线程之间的隔离。下面两张图基本上把ThreadLocal的结构介绍清楚了。接下来我们看一下ThreadLocalMap中的数据结构。我们知道HaseMap是通过链表和红黑树(jdk1.8)来解决hash冲突的,但是我们看到ThreadLocalMap只有一个数组。它是如何解决hash冲突的呢?ThreadLocalMap采用的是“线性检测”的方式。什么是线性检测?就是根据初始key的hashcode值来确定元素在表数组中的位置。如果发现该位置已经有其他key值的元素被占用,则采用固定算法以一定步长寻找下一个位置,依次判断,直到找到可以入库的位置。ThreadLocalMap解决Hash冲突的方式就是简单的在步长上加1或者减1,找到下一个相邻的位置。/***Incrementimodulolen.*/privatestaticintnextIndex(inti,intlen){return((i+1=0)?i-1:len-1);}这样,如果一个线程中有大量的ThreadLocal,就会出现性能问题,因为每次都需要遍历表,清空是无效值。所以我们在使用的时候尽量少用ThreadLocal,不要在线程中创建大量的ThreadLocal。如果我们需要设置不同的参数类型,我们可以使用ThreadLocal来存储一个Object的Map。这样可以大大减少ThreadLocal的创建数量。.伪代码如下:publicfinalclassHttpContext{privateHttpContext(){}privatestaticfinalThreadLocal