深度解析ThreadLocal原理

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"今天呢,和大家聊一下"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 是什么?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"JDK1.2"}]},{"type":"text","text":"提供的的一个线程绑定变量的类。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"他的思想就是:给每一个使用到这个资源的线程都克隆一份,实现了不同线程使用不同的资源,且该资源之间相互独立"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. 为什么用?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"思考一个场景"},{"type":"text","text":":数据库连接的时候,我们会创建一个"},{"type":"codeinline","content":[{"type":"text","text":"Connection"}]},{"type":"text","text":"连接,让不同的线程使用。这个时候就会出现多个线程争抢同一个资源的情况。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这种多个线程争抢同一个资源的情况,很常见,我们常用的解决办法也就两种:"},{"type":"text","marks":[{"type":"strong"}],"text":"空间换时间,时间换空间"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"没有办法,鱼与熊掌不可兼得也。就如我们的"},{"type":"codeinline","content":[{"type":"text","text":"CAP"}]},{"type":"text","text":"理论,也是牺牲其中一项,保证其他两项。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而针对上面的场景我们的解决办法如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"空间换时间:为每一个线程创建一个连接。直接在线程工作中,创建一个连接。("},{"type":"text","marks":[{"type":"strong"}],"text":"重复代码太多"},{"type":"text","text":")使用"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":",为每一个线程绑定一个连接。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"时间换空间:对当前资源加锁,每一次仅仅存在一个线程可以使用这个连接。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"为每一个线程绑定一个指定类型的变量,相当于线程私有化"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. 怎么用?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"ThreadLocal threadLocal = new ThreadLocal<>();\nthreadLocal.get();\nthreadLocal.set(1);\nthreadLocal.remove();\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"没错,这四行代码已经把"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"的使用方法表现得明明白白。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"get"}]},{"type":"text","text":"从"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"拿出一个当前线程所拥有得对象"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"set"}]},{"type":"text","text":"给当前线程绑定一个对象"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"remove"}]},{"type":"text","text":"将当前线程绑定的当前对象移除"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"记住在使用的以后,一定要remove,一定要remove,一定要remove"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为什么要"},{"type":"codeinline","content":[{"type":"text","text":"remove"}]},{"type":"text","text":"。相信不少小伙伴听到过"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"会导致内存泄漏问题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"没错,所以为了解决这种情况,"},{"type":"text","marks":[{"type":"strong"}],"text":"所以你懂吧,用完就移除,别浪费空间(渣男欣慰)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到这,脑袋上有好多问号出现了("},{"type":"text","marks":[{"type":"strong"}],"text":"小朋友你是否有很多问号?"},{"type":"text","text":")"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"为啥会引发内存泄漏?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"为啥不remove就内存泄漏了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"它是怎么讲对象和线程绑定的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"为啥get的时候拿到的就是当前线程的而不是其他线程的"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"它怎么实现的???"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"来吧,"},{"type":"text","marks":[{"type":"strong"}],"text":"开淦,源码来"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4. 源码解读"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先来说一个思路:"},{"type":"text","marks":[{"type":"strong"}],"text":"如果我们自己写一个"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"会咋写?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"线程绑定一个对象。**这难道不是我们熟知的"},{"type":"codeinline","content":[{"type":"text","text":"map"}]},{"type":"text","text":"映射?**有了"},{"type":"codeinline","content":[{"type":"text","text":"Map"}]},{"type":"text","text":"我们就可以以线程为"},{"type":"codeinline","content":[{"type":"text","text":"Key"}]},{"type":"text","text":",对象为"},{"type":"codeinline","content":[{"type":"text","text":"value"}]},{"type":"text","text":"添加到一个集合中,然后各种"},{"type":"codeinline","content":[{"type":"text","text":"get,set,remove"}]},{"type":"text","text":"操作,想怎么玩就怎么玩,搞定。"},{"type":"text","marks":[{"type":"strong"}],"text":"😀"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"这个时候,有兄弟说了。你这思路不对啊,你这一个线程仅仅只能存放一个类型的变量,那我想存多个呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"摸摸自己充盈的发量,你说出了一句至理名言:"},{"type":"text","marks":[{"type":"strong"}],"text":"万般问题,皆系于源头和结果之中。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从结果考虑,让开发者自己搞线程私有(估计被会开发者骂死)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"来吧,从源头考虑。现在我们的需求是:"},{"type":"text","marks":[{"type":"strong"}],"text":"线程可以绑定多个值,而不仅仅是一个"},{"type":"text","text":"。嗯,没错,兄弟们把你们的想法说出来。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"让线程自己维护一个Map,将这个"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"作为"},{"type":"codeinline","content":[{"type":"text","text":"Key"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":",对象作为"},{"type":"codeinline","content":[{"type":"text","text":"Value"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"不就搞定了"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"兄弟,牛掰旮旯四"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此时,又有兄弟说了。按照你这样的做法,将"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"扔到线程本身的的Map里,那岂不是这个"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","marks":[{"type":"strong"}],"text":"一直被线程对象引用,所以在线程销毁之前都是可达的,都无法"},{"type":"codeinline","content":[{"type":"text","text":"GC"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"呀,有"},{"type":"codeinline","content":[{"type":"text","text":"BUG"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"啊"},{"type":"text","text":"???"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"**好,问题。**这样想,既然由于线程和"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"对象存在引用,导致无法"},{"type":"codeinline","content":[{"type":"text","text":"GC"}]},{"type":"text","text":",那我将你和线程之间的引用搞成弱引用或者软引用不就成了。一"},{"type":"codeinline","content":[{"type":"text","text":"GC"}]},{"type":"text","text":"你就没了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"啥,你不知道啥是弱引用和软引用???"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面讲过的东西,算啦再给你们复习一波。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"JDK"}]},{"type":"text","text":"中存在四种类型引用,默认是强引用,也就是我们经常干的事情。疯狂"},{"type":"codeinline","content":[{"type":"text","text":"new,new,new"}]},{"type":"text","text":"。这个时候创建的对象都是强引用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"强引用。直接"},{"type":"codeinline","content":[{"type":"text","text":"new"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"软引用。通过"},{"type":"codeinline","content":[{"type":"text","text":"SoftReference"}]},{"type":"text","text":"创建,在内存空间不足的时候直接销毁,即它可能最后的销毁地点是在老年区"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弱引用。通过"},{"type":"codeinline","content":[{"type":"text","text":"WeakReference"}]},{"type":"text","text":"创建,在"},{"type":"codeinline","content":[{"type":"text","text":"GC"}]},{"type":"text","text":"的时候直接销毁。即其销毁地点必定为伊甸区"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虚引用。通过"},{"type":"codeinline","content":[{"type":"text","text":"PhantomReference"}]},{"type":"text","text":"创建,它和不存也一样,"},{"type":"text","marks":[{"type":"strong"}],"text":"非常虚,只能通过引用队列在进行一些操作,主要用于堆外内存回收"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"好了,回到正题,上面的引用里最适合我们当前的场景的就是弱引用了,"},{"type":"text","marks":[{"type":"strong"}],"text":"为什么这个样子说:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在以往我们使用完对象以后等着"},{"type":"codeinline","content":[{"type":"text","text":"GC"}]},{"type":"text","text":"清理,但是对于"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"来说,即使我们使用结束,也会因为线程本身存在该对象的引用,处于对象可达状态,垃圾回收器无法回收。这个时候当"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"太多的时候就会出现内存泄漏的问题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而我们将"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"对象的引用作为弱引用,那么就很好的解决了这个问题。当我们自己使用完"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"以后,"},{"type":"text","marks":[{"type":"strong"}],"text":"当"},{"type":"codeinline","content":[{"type":"text","text":"GC"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"的时候就会将我们创建的强引用直接干掉,而这个时候我们完全可以将线程"},{"type":"codeinline","content":[{"type":"text","text":"Map"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"中的引用干掉,于是使用了弱引用,这个时候大家应该懂了为啥不使用软引用了吧"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"还有一个问题:为什么会引发内存泄漏呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"了解"},{"type":"codeinline","content":[{"type":"text","text":"Map"}]},{"type":"text","text":"结构的兄弟们应该清楚,内部实际就一个节点数组,对于"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocalMap"}]},{"type":"text","text":"而言,内部是一个"},{"type":"codeinline","content":[{"type":"text","text":"Entity"}]},{"type":"text","text":",它将"},{"type":"codeinline","content":[{"type":"text","text":"Key"}]},{"type":"text","text":"作为弱引用,"},{"type":"codeinline","content":[{"type":"text","text":"Value"}]},{"type":"text","text":"还是强引用。如果我们在使用完"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}]},{"type":"text","text":"以后,没有对"},{"type":"codeinline","content":[{"type":"text","text":"Entity"}]},{"type":"text","text":"进行移除,会引发内存泄漏问题。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"ThreadLocalMap"}]},{"type":"text","text":"提供了一个方法"},{"type":"codeinline","content":[{"type":"text","text":"expungeStaleEntry"}]},{"type":"text","text":"方法用来排除无效的"},{"type":"codeinline","content":[{"type":"text","text":"Entity"}]},{"type":"text","text":"("},{"type":"codeinline","content":[{"type":"text","text":"Key"}]},{"type":"text","text":"为空的实体)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"说到这里,有一个问题我思考了蛮久的,value为啥不搞成弱引用,用完直接扔了多好"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最后思考出来得答案(按照源码推了一下):"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"不设置为弱引用,是因为不清楚这个"},{"type":"codeinline","content":[{"type":"text","text":"Value"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"除了"},{"type":"codeinline","content":[{"type":"text","text":"map"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"的引用还是否还存在其他引用,如果不存在其他引用,当"},{"type":"codeinline","content":[{"type":"text","text":"GC"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"的时候就会直接将这个Value干掉了,而此时我们的"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"还处于使用期间,就会造成Value为null的错误,所以将其设置为强引用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而为了解决这个强引用的问题,它提供了一种机制就是上面我们说的将"},{"type":"codeinline","content":[{"type":"text","text":"Key"}]},{"type":"text","text":"为"},{"type":"codeinline","content":[{"type":"text","text":"Null"}]},{"type":"text","text":"的"},{"type":"codeinline","content":[{"type":"text","text":"Entity"}]},{"type":"text","text":"直接清除"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"到这里,这个类的设计已经很清楚了。接下来我们看一下源码吧!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要注意的一个点是:"},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocalMap"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"解决哈希冲突的方式是线性探测法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"人话就是:如果当前数组位有值,则判断下一个数组位是否有值,如果有值继续向下寻找,直到一个为空的数组位"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Set方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"class ThreadLocal\t\n\tpublic void set(T value) {\n \t//拿到当前线程\n Thread t = Thread.currentThread();\n //获取当前线程的ThreadLocalMap\n ThreadLocalMap map = getMap(t);\n if (map != null)\n //如果当前线程的Map已经创建,直接set\n map.set(this, value);\n else\n //没有创建,则创建Map\n createMap(t, value);\n }\n\n\tprivate void set(ThreadLocal> key, Object value) {\n Entry[] tab = table;\n int len = tab.length;\n int i = key.threadLocalHashCode & (len-1);\n\t\t\t//拿到当前数组位,当前数组位是否位null,如果为null,直接赋值,如果不为null,则线性查找一个null,赋值\n for (Entry e = tab[i];\n e != null;\n e = tab[i = nextIndex(i, len)]) {\n ThreadLocal> k = e.get();\n\n if (k == key) {\n e.value = value;\n return;\n }\n\n if (k == null) {\n replaceStaleEntry(key, value, i);\n return;\n }\n }\n\n tab[i] = new Entry(key, value);\n int sz = ++size;\n //清除一些失效的Entity\n if (!cleanSomeSlots(i, sz) && sz >= threshold)\n rehash();\n }\n\n\n\tThreadLocalMap getMap(Thread t) {\n //获取当前线程的ThreadLocalMap\n return t.threadLocals;\n }\n\n\tvoid createMap(Thread t, T firstValue) {\n \t//当前对象作为Key,和我们的设想一样\n t.threadLocals = new ThreadLocalMap(this, firstValue);\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Get方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\tpublic T get() {\n //获取当前线程\n Thread t = Thread.currentThread();\n //拿到当前线程的Map\n ThreadLocalMap map = getMap(t);\n if (map != null) {\n //获取这个实体\n ThreadLocalMap.Entry e = map.getEntry(this);\n if (e != null) {\n @SuppressWarnings(\"unchecked\")\n T result = (T)e.value;\n //返回\n return result;\n }\n }\n return setInitialValue();\n }\n\n\tprivate Entry getEntry(ThreadLocal> key) {\n //计算数组位\n int i = key.threadLocalHashCode & (table.length - 1);\n Entry e = table[i];\n //如果当前数组有值,且数组位的key相同,则返回value\n if (e != null && e.get() == key)\n return e;\n else\n //线性探测寻找对应的Key\n return getEntryAfterMiss(key, i, e);\n }\n\n\tprivate Entry getEntryAfterMiss(ThreadLocal> key, int i, Entry e) {\n Entry[] tab = table;\n int len = tab.length;\n\n while (e != null) {\n ThreadLocal> k = e.get();\n if (k == key)\n return e;\n if (k == null)\n //排除当前为空的Entity\n expungeStaleEntry(i);\n else\n //获取下一个数组位\n i = nextIndex(i, len);\n e = tab[i];\n }\n //如果没有找到直接返回空\n return null;\n }\n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"remove"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"\tpublic void remove() {\n ThreadLocalMap m = getMap(Thread.currentThread());\n if (m != null)\n m.remove(this);\n }\n\n\tprivate void remove(ThreadLocal> key) {\n Entry[] tab = table;\n int len = tab.length;\n int i = key.threadLocalHashCode & (len-1);\n //拿到当前的数组,判断是否为需要的数组位,如果不是线性查找\n for (Entry e = tab[i];\n e != null;\n e = tab[i = nextIndex(i, len)]) {\n if (e.get() == key) {\n e.clear();\n //清空位NUll的实体\n expungeStaleEntry(i);\n return;\n }\n }\n }\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"我们可以看到一个现象:在"},{"type":"codeinline","content":[{"type":"text","text":"set"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":","},{"type":"codeinline","content":[{"type":"text","text":"get"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":","},{"type":"codeinline","content":[{"type":"text","text":"remove"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"的时候都调用了"},{"type":"codeinline","content":[{"type":"text","text":"expungeStaleEntry"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"来将所有失效的"},{"type":"codeinline","content":[{"type":"text","text":"Entity"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"移除"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看一下这个方法做了什么"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"private int expungeStaleEntry(int staleSlot) {\n Entry[] tab = table;\n int len = tab.length;\n\n // 删除实体的Value\n tab[staleSlot].value = null;\n //置空这个数组位\n tab[staleSlot] = null;\n //数量减一\n size--;\n\n // 重新计算一次哈希,如果当前数组位不为null,线性查找直到一个null\n Entry e;\n int i;\n for (i = nextIndex(staleSlot, len);\n (e = tab[i]) != null;\n i = nextIndex(i, len)) {\n ThreadLocal> k = e.get();\n if (k == null) {\n e.value = null;\n tab[i] = null;\n size--;\n } else {\n int h = k.threadLocalHashCode & (len - 1);\n if (h != i) {\n tab[i] = null;\n\n // Unlike Knuth 6.4 Algorithm R, we must scan until\n // null because multiple entries could have been stale.\n while (tab[h] != null)\n h = nextIndex(h, len);\n tab[h] = e;\n }\n }\n }\n return i;\n }"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"看完三件事❤️"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":""}]},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"关注公众号 『 "},{"type":"text","marks":[{"type":"strong"}],"text":"java烂猪皮"},{"type":"text","text":" 』,不定期分享原创知识。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"同时可以期待后续文章ing🚀"}]}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/34/34172ad7f3cc8e0f28bd1fc6ca2d2b68.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文出处:https://my.oschina.net/onlyzuo/blog/4704594"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章