日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ThreadLocal不仅要应付面试,更要真的理解,真的会用

發(fā)布時間:2024/7/23 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ThreadLocal不仅要应付面试,更要真的理解,真的会用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

記得我?guī)啄昵暗谝淮蚊嬖嚨臅r候,就是被問了這個,記得面試官直接就讓我說說ThreadLocal的實現(xiàn)原理以及平時有沒有見過哪些地方用到了。
我當(dāng)時初入職場,還是一個大菜鳥,所以直接就被干蒙了,至今還記憶猶新。

閑來無事,總結(jié)一下這塊,其實仔細(xì)想想這個ThreadLocal,整體思路其實挺清晰的,但有些細(xì)節(jié)會有難度,可能會涉及到一些比較深的平時不用的知識,說實話我也還沒有完全理清楚,但一直都在努力中。

概念

定義

我們說的ThreadLocal是java.lang包下的一個類,這個類提供特殊的線程局部變量,使得每個訪問該變量的線程在其內(nèi)部都有一個獨立的初始化變量副本。
用人話解釋:
先說普通類中定義的變量,我們都知道是多個線程共有的。
而ThreadLocal這個類中有個特殊的變量,特殊就特殊在針對不同的線程,在用這個ThreadLocal的時候,都能拿到本線程獨有的值,你可以set,可以get,線程之間互不影響。

其實ThreadLocal這個概念,并不是java語言獨有的,其實很多語言都有這個概念,只不過java中是用哈希表實現(xiàn)了這個概念。

特點

簡單,開銷小,線程安全。

哪里用ThreadLocal

1、Quartz的SimpleSemaphore,提供資源隔離



看上圖:
SimpleSemaphore里面就有個方法:obtrainLock方法,用synchronized鎖
這個方法中有個很重的while操作(消費者處理完所有事情,需要等待新的事情,這個等待是一個while循環(huán))
lockName是這個方法的入?yún)?#xff0c;這個while方法的判斷邏輯是如果locks這個HashSet中有這個lockName,這個線程就執(zhí)行wait()方法,由于obtrainLock本身是一個所方法,然后再去執(zhí)行wait(),你的線程就被完全阻塞在這里排隊了。
試想,如果沒有ThreadLocal先過濾,那么同一個線程的多次調(diào)用這個obtrainLock方法,帶著相同的lockName,就會多次進(jìn)入這個while循環(huán),其實同一個線程是不需要多次進(jìn)入這個操作的
所以通過在這個加鎖操作之前用ThreadLocal判斷(isLockOwner方法),將同一個線程帶著相同lockName調(diào)用這個方法的次數(shù),就減少到一次了,即只會第一次進(jìn)入while循環(huán),其他的都被isLockOwner方法擋住了
最終使得訪問后面很重的操作的頻率大大降低,算是一個優(yōu)化。

2、Mybatis的SqlSessionManager,資源持有

我們知道Mybatis連數(shù)據(jù)庫后,會有個連接池,里面會維護(hù)有多個連接,每次操作數(shù)據(jù)庫,都需要拿到連接,再去操作,拿連接就是那個sqlSession.getConnection方法,每次操作都可能拿到任何一個連接。
如果想要支持事務(wù),那必須讓一次事務(wù)的所有操作,都必須讓同一個連接處理,這樣才能要么一起成功,要么一起失敗,而一次事務(wù)的每個操作都需要從線程池中拿連接,那如何保證一次事務(wù)的每次操作拿到的都是同一個連接呢?
一次事務(wù)的多個操作一般都是一個線程去執(zhí)行的,那其實問題就變成如何保證一個線程拿到的總是相同的一個連接,這里就用到了ThreadLocal,將當(dāng)前線程拿到的連接保存在ThreadLocal中,下次該線程拿連接,就直接從ThreadLocal中拿這個連接,這樣就保證了同一個線程永遠(yuǎn)拿到同一個連接,而其他線程拿哪個連接不受這個線程的影響。

我們看看具體的代碼實現(xiàn):
先是定義ThreadLocal,存放的就是SqlSession,每一個連接對應(yīng)一個SqlSession

然后開始將一個線程的SqlSession放入ThreadLocal中

真正用的時候,比如commit,rollback等方法,就都從ThreadLocal中獲取連接了。

3、Spring的TransactionContextHolder


TransactionContext也叫分布式事務(wù)資源池,保存的是當(dāng)前環(huán)境的上下文,里面有個PlatformTransactionManager,這個就是執(zhí)行commit和rollback的類,所以在分布式事務(wù)中也要保住同一個線程用同一個PlatformTransactionManager去執(zhí)行commit或rollback,所以最終TransactionContext用ThreadLocal保存起來,達(dá)到效果。

4、登錄

登錄的時候,可以把每個線程的登錄信息放在ThreadLocal中,就保證了同一個人的操作始終在同一個線程中。

ThreadLocal核心源碼解讀

1、首先,每個Thread中,都有一個成員變量threadLocals

這個是專門為ThreadLocal加的,具體threadLocals的賦值過程,是在ThreadLocal中
threadLocals的類型是ThreadLocal.ThreadLocalMap,這個ThreadLocalMap是ThreadLocal中的自定義的一個內(nèi)部map類,key是ThreadLocal對象,value是每個線程的那個獨有的變量副本。

2、ThreadLocal的get方法


先拿到當(dāng)前線程
getMap方法,就是從當(dāng)前線程中拿ThreadLocalMap,這個就是Thread中那個成員變量。
ThreadLocalMap的key是當(dāng)前這個ThreadLocal對象,value就是我們這個get方法真正要返回的值。
如果能拿到ThreadLocalMap,那么就返回ThreadLocalMap中當(dāng)前ThreadLocal對象對應(yīng)的value值。
如果拿不到ThreadLocalMap,就去初始化value,最后再返回value。

總結(jié)

我們看到ThreadLocal的實現(xiàn),就能清楚的知道為什么ThreadLocal可以保存不同線程的不同值了。
是因為其實最終這些值還是保存在了各個線程中的一個map中,而ThreadLocal僅僅是作為這個map的一個key。
那么對于一個線程,如果他遇到多個ThreadLocal,其實線程中的那個map就有多對值了。
有沒有一種反向操作的感覺,乍一看以為這些值都是保存在ThreadLocal中的,最終發(fā)現(xiàn)還是在線程中保存。

注意

要注意的是,每個線程中的ThreadLocalMap是ThreadLocal中定義的一個靜態(tài)類,相當(dāng)于ThreadLocal重寫了一個map,那有人會問了,為什么不直接用HashMap呢?

其實這是一個涉及到j(luò)ava垃圾回收的問題,重寫的這個ThreadLocalMap,主要就是為了這個事情搞的。

我們知道其實HashMap中真正的數(shù)據(jù)是在一個個Entry中的,其實ThreadLocalMap也是這樣,只不過ThreadLocalMap中的Entry是繼承了WeakReference這個類。我們知道ThreadLocalMap中的key值,其實是ThreadLocal對象,在set某個對象的時候,需要根據(jù)這個對象的hash值去hash表中找槽,如果找到對應(yīng)的槽后,槽上原來的對象被回收了,那對于的hash表上的位置的值就是null,那么ThreadLocalMap就會對這種已經(jīng)廢棄掉的null值對應(yīng)的槽做一些處理(主要是重新回收這些槽,并重新分配hash表大小等)。這樣相當(dāng)于同步了垃圾回收的結(jié)果。

這就是為什么要重寫hashMap了,因為hashMap不會處理這些邏輯,不處理就會造成槽不斷的被已經(jīng)回收的ThreadLocal的空對象占用著釋放不出來,最后影響hash的查找,因為時間久了,每次正常hash后應(yīng)該放的槽都被null占了,只能繼續(xù)向后移著放。

總結(jié)

以上是生活随笔為你收集整理的ThreadLocal不仅要应付面试,更要真的理解,真的会用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。