1.背景最近有人问我ThreadLocal是如何隔离各个线程中的值的。这里有一篇文章来简单记录一下。2、ThreadLocal解决的问题数据属于线程Thread本身,其他线程无法影响。(注意:需要调用ThreadLocal的remove方法)不存在线程安全问题。(因为ThreadLocal类型的变量只能被自己的线程访问,所以是这样的。)例如:用户登录成功后,需要保存登录的用户信息,以便在任何地方使用系统,那么此时可以使用ThreadLocal来实现。例如:SpringSecurity中的ThreadLocalSecurityContextHolderStrategy类。3.如何创建ThreadLocal实例privatestaticfinalThreadLocalUSER_NAME=newThreadLocal<>();ThreadLocal实例推荐使用privatestaticfinal修饰。四、ThreadLocal如何实现线程变量隔离1、理解ThreadLocal这三个类:该类提供了简单的set、get、remove方法,用于设置、获取或移除线程局部变量绑定的值。ThreadLocalMap:这是ThreadLocal中定义的一个类,可以简单的理解为一个Map,但是它的key是一个WeakReference弱引用类型,这样当value没有被其他地方引用的时候,发生垃圾回收的时候,这个map的key就会会被自动回收,但它的值不会被自动回收。staticclassEntryextendsWeakReference>{对象值;Entry(ThreadLocal>k,Objectv){//键弱引用super(k);//valuestrongreferencevalue=v;}}Thread:这是一个线程类。该类中有一个threadLocals变量,具体类型为ThreadLocal.ThreadLocalMap。2.看set方法是如何实现的publicvoidset(Tvalue){//获取当前线程Threadt=Thread.currentThread();//获取线程本身绑定的ThreadLocalMap,它是从Thread类派生的ThreadLocalMap从`threadLocals`变量中获取map=getMap(t);if(map!=null){//将value设置为map,key是ThreadLocal对象的一个??实例。map.set(this,value);}else{//如果地图不存在,则创建它。createMap(t,值);}}通过上面的代码,我们可以知道:当我们在ThreadLocal中设置一个值时,会经过以下几个步骤:获取当前线程Thread获取当前线程的ThreadLocalMap对象。将value设置为ThreadLocalMap,key是ThreadLocal对象,value是具体的值。3.看get方法如何实现publicTget(){//获取当前线程Threadt=Thread.currentThread();//获取线程自身绑定的ThreadLocalMap对象ThreadLocalMapmap=getMap(t);if(map!=null){//这是一个ThreadLocal对象,获取Map中的Entry对象ThreadLocalMap.Entrye=map.getEntry(this);if(e!=null){@SuppressWarnings("unchecked")//得到具体的ValueTresult=(T)e.value;返回结果;}}//设置初始值returnsetInitialValue();}从上面的get和set方法可以知道,通过设置或者获取ThreadLocal对象中的值,其实最终是操作到Thread中的threadLocals字段object,而这个字段属于Thread本身,所以是隔离的。五、如何处理ThreadLocalMap中的hash冲突1、ThreadLocal对象的hash值是多少?privatefinalintthreadLocalHashCode=nextHashCode();//ThreadLocal对象本身的哈希码值privatefinalintthreadLocalHashCode=nextHashCode();//从0开始privatestaticAtomicIntegernextHashCode=newAtomicInteger();//每次增加一个固定值privatestaticfinalintHASH_INCREMENT=0x61c88647;//哈希码值计算privatestaticintnextHashCode(){returnnextHashCode.getAndAdd(HASH_INCREMENT);}从上面的代码可以看出,ThreadLocal类实例化后,其哈希码值(threadLocalHashCode)是固定的。即使ThreadLocal调用了set方法,设置了其他值,它的哈希码值也不会改变。该字段threadLocalHashCode是ThreadLocal对象的hash值,需要在ThreadLocalMap中使用。2、解决hash冲突ThreadLocalMap解决hash冲突的方法很简单。它是通过线性检测方法。如果有冲突,就在数组后面找一个可用的位置。有关详细信息,请参见上图。演示就是A和B有两个ThreadLocal对象,然后发生冲突,A和B存在的位置就在那个地方。6、ThreadLocal内存泄漏ThreadLocal为什么会出现内存泄漏?这是因为ThreadLocalMap中的key是WeakReference类型,也就是弱引用类型,如果没有外部强引用类型,gc的时候会自动回收弱引用类型的数据。注意:此时key被回收了,value没有被回收。因此,在ThreadLocalMap中的Entry[]中,可能存在key为null,但value为具体值的对象,从而发生内存泄漏。解决内存泄漏:当我们使用完ThreadLocal对象后,需要在合适的时候调用ThreadLocal#remove()方法。否则只能在Thread自动退出后才能清除。如果使用线程池,Thread会被复用,清除的机会就更难了。