ThreadLocal介绍:
- ThreadLocal提供了线程的局部变量,让每个线程都能够通过get/set办法来对局部变量的数据进行操作,不会和其余线程的局部变量产生抵触,实现了线程的数据隔离,比方超市的公共储物柜,每个人都能够应用,然而每个人的物品有都是分隔开来的
ThreadLocal实现原理:
通过源码能够察看到,当调用ThreadLocal的set办法时,去调用了getMap办法,传入了以后线程,返回了ThreadLocalMap
- 这个ThreadLocalMap其实就是存在于Thread类外面的一个Map
- 获取到ThreadLocalMap之后,将本人(ThreadLocal)作为key,值作为value放入ThreadLocalMap中,这样就能起到线程之间数据隔离的成果
发问工夫:
Thread有什么用呢?或者说利用场景在哪?
- 在我的项目中,ThreadLocal的确 很(根)少(本)用(不)到(用),在Spring中有ThreadLocal的实现,ThreadLocal存储的是一个Map,Map外面是一个个的Entry,在Spring中实现中,Entry中的Key寄存的是DataSource,Value寄存的是Connection,用ThreadLocal是为了保障一个线程获取一个Connection连贯对象,从而就能保障一次事务内所有的操作都是在同一个数据库连贯上
刚刚提到ThreadLocalMap是定义在Thread中的,那么我能够在ThreadLocal中定义这个Map,将以后线程作为key,值作为value,不也能实现线程之间数据隔离吗?如下图:
实践上来说是能够的,然而要思考以下三点
- 每个线程都会有多个公有变量,既然要以以后线程为Key,那么就要辨别多个变量,须要保护一个标识
- 就算解决了保护标识的问题,还有就是当并发足够大时,多个线程来拜访本人存储的公有变量时,多个线程拜访这个Map,可能会导致Map体积收缩,从而导致Map的拜访性能有所降落
- 而且这个Map保护着所有线程的公有变量,所以不晓得什么时候能够销毁它
为什么key要用弱援用指向ThreadLocal对象?什么是弱援用?
首先,先介绍一下Java的四中援用:Java有强、软、弱、虚四中援用
- 强援用(NormalReference):强引指向一个对象时,只有对象没有被置为null,那么在GC时就不会被回收
- 软援用(SoftReference):当有足够的内存时,这个援用指向的这个对象就不会被回收,当内存不足时,这块援用指向的对象就会被回收
- 弱援用(WeakReference):只有触发GC,这个援用指向的对象就会被回收
- 虚援用(PhantomReference):虚援用次要的作用是跟踪垃圾回收的状态,当回收时通过援用队列做一些告诉类的工作【迷迷糊糊,不太了解】
其次咱们来看,为什么要用弱援用指向ThreadLocal对象呢
- 假如key用的是强援用指向的是ThreadLocal对象,那么当Thread与ThreadLocal之间的援用断掉,又因为ThreadLocalMap是存在于Thread中,只有Thread不隐没,这个ThreadLocalMap就不会隐没,ThreadLocalMap中这个entry就会始终存在从而导致内存透露
- 当初是key用的弱援用指向的ThreadLocal对象,当Thread与ThreadLocal之间的援用断掉,key与ThreadLocal之间的援用就会主动隐没,不会呈现内存透露的问题
然而ThreadLocal还是会有内存泄露的问题呈现,当ThreadLocal对象被回收,key的值变为了null,导致绝对应的value再也无奈被拜访到,因而还是会呈现内存透露的问题,小伙伴不要把 内存透露 和 内存溢出(OOM)搞混了
- 内存透露:有一块内存不必了,然而永远无奈被回收
- 内存溢出(OOM):内存越申请越多,越申请越多,最初内存被占满,无奈再申请到内存了,就会呈现内存溢出OOM
刚刚说ThreadLocal会呈现内存透露,那么会导致内存长期透露吗?
- 这个是不会的,因为ThreadLocalMap是属于Thread的,只有将Thread销毁,ThreadLocalMap也会跟着被销毁,而且ThreadLocal也有一些“爱护”措施,就是在操作ThreadLocal时,发现key为null时,会将其革除掉,所以大家不必放心会呈现长期性内存透露问题
那么长期性的内存透露是在什么条件下产生的呢,必须要同时满足以下几点才会导致长期性的内存透露
- ThreadLocal被回收
- 线程复用(线程池的环境下)
- 线程复用后不再调用ThreadLocal的set/get/remove办法
说了这么多,那么,你学废了吗?
不点个赞再走嘛