通俗易懂讲解ThreadLocal,不懂的过来打我

先举个例子,ThreadLocal是啥呢?在一个线程的情况下,threadlocal就相当于一个仓库,很简单,就是一个保存值的局部变量,通过set get 方法来从这个仓库中存储和获取。 但是在多个线程的情况下,就很不一样了,threadlocal就变成了孙悟空,对,你没看错,变成了孙悟空,为啥是孙悟空呢?因为孙悟空会分身术,会将自己变成多个孙悟空,而且变出来的每个孙悟空都各自具有各自的思想,是完全独立的,这个多个孙悟空之间不存在任何的联系。有多少个线程就有多少个孙悟空,每个线程里的孙悟空虽然名字都叫做孙悟空,但是他们之间没有任何的联系!!!

在这里插入图片描述在这里插入图片描述在这里插入图片描述

代码演示

在这里插入图片描述在这里插入图片描述
此时content局部变量还没有用到threadlocal
则打印的结果可能会发生错乱,如下图所示
在这里插入图片描述在这里插入图片描述

再次打印,结果就不会发生错乱了
在这里插入图片描述

重点来啦

在这里插入图片描述

synchronized是只有一份资源,多个人抢着来用,因此加锁,排队!比如多个人要去上厕所,但只有一个坑,因此每次只能一个人去上,上的时候怕别人乱闯进来,因此要加把锁
threadlocal是复制多个副本,供多个人一起使用。现在就是有多少个人,就有多少个坑了,可以都同时上厕所

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

这种方法的弊端 1、降低代码耦合性,参数传递使得service层和dao层之间有了直接的联系 2、降低程序性能,通过synchronized关键字使得程序失去了并发性,只能一个线程一个线程的去执行

在这里插入图片描述在这里插入图片描述

总结一下:1、传递数据,相当于仓库 2、线程隔离,因为它是孙悟空!!

ThrealLocal原理

下面那个图不知道有没有看懂,那一个一个的格子,就相当于一个一个的key-value,那一个一个的格子组成的长条,就是一个map

通俗的讲,以前的那种设计是 以ThreadLocal为基准的,也就是上面说的仓库,其实这里理解为仓库更为准确,因为无论多少个线程都只有一个Threadlocal,所以相当于一个仓库,虽然是多线程,但是他没有变成孙悟空,还是勤勤恳恳的做一个仓库,以这个仓库为老大,一切以
仓库着重考虑,所以每创建一个线程,仓库里面的Map(注意这里的Map 刚开始是没有Map的,是第一次使用set的时候创建的)就多一个entry键值对。。。注意这里,无论有多少个线程,都只有一个Map, 以新创建的thread为key,以存入的数为value,反正Threadlocal是大佬级别的,一切以Threadlocal为主

而现在呢?一切以Thread为主,现在是Thread为大佬,每创建一个线程,线程都会创建一个Map,Map的key存储Threadlocal,也就是你上面说的孙悟空,多个线程就有多个Threadlocal,因为多个线程就有多个Map,而Map的key就是这个Threadlocal

在这里插入图片描述在这里插入图片描述在这里插入图片描述

set源码解读

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

说明一个ThreadLocal 和 ThreadLocalMap 和 Thread 的区别,看源码的时候,好多人都被几个玩意儿弄混了,现在说明一下:

ThreadLocal是一个类

在这里插入图片描述
ThreadLocalMap类是ThreadLocal的内部类,set和get方法是ThreadLocal的成员方法,所以在刚开始没有Map的时候,是可以直接在set方法里面创建的,但是之后需要用到Map的时候,Map是在Thread类里面的成员变量,Thread类里面的成员变量threadlocals的类型就是
ThreadLocalMap,所以直接通过Thread t 的 t.threadlocals就得到了这个ThreadLocalMap

ThreadLocalMap源码分析

threadLocalMap源码是最复杂的,我们从3个方面看下
在这里插入图片描述在这里插入图片描述

table是一个数组,里面元素类型是Entry类型
而Entry是一个类,存储的是键值对

在这里插入图片描述

强引用不会被回收,弱引用会被回收

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
这里需要注意的是,有两点 。 第一点: threadlocal定义的时候是以局部变量的方式定义,所以threadlocal类型的变量引用是保存在栈中。 第二点:每一个线程都有自己的threadlocalMap,而当前线程的这个ThreadlocalMap的key就是threadlocal,所以就有threadRef->currentThread->threadLocalMap->entry

Entry中的threadLocal是弱引用的时候,这时候当ThreadLocal使用完毕的时候, ThreadLocal Ref 因为是在栈中,所以自然被退出栈了,ThreadLocal退栈之后,因为threadlocal是弱引用,就会使得ThreadLocal被垃圾回收,threadlocal回收之后,就相当于没有了key,这时候这个Entry的Key就没有了,就是null了

最关键的是还有一个当前正在运行的线程指向的Map,map里面的value不会被回收,所以也有可能是内存泄漏在这里插入图片描述

重点知识点:弱引用会使得key为null ,这样下一次set() 和 get() 时,就会使得value也为null ,即value在下一次map调用set,get方法时会被清除,从而有效的避免了内存泄漏

既然用到了Map结构,就一定会有hash冲突的问题,因为Map结构其实就是数组+链表(红黑树),数组里面存放的就是

Entry(键值对),那该怎么存放呢?键值对该数组的哪个位置呢?这里就涉及到了hash冲突
在这里插入图片描述

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42350785/article/details/106727821