您好,登錄后才能下訂單哦!
說明
面試官:講講你對ThreadLocal的一些理解。
那么我們該怎么回答呢????你也可以思考下,下面看看零度的思考;
ThreadLocal用在什么地方?
討論ThreadLocal用在什么地方前,我們先明確下,如果僅僅就一個(gè)線程,那么都不用談ThreadLocal的,ThreadLocal是用在多線程的場景的?。。?/p>
ThreadLocal歸納下來就2類用途:
保存線程上下文信息,在任意需要的地方可以獲?。。?!
由于ThreadLocal的特性,同一線程在某地方進(jìn)行設(shè)置,在隨后的任意地方都可以獲取到。從而可以用來保存線程上下文信息。
常用的比如每個(gè)請求怎么把一串后續(xù)關(guān)聯(lián)起來,就可以用ThreadLocal進(jìn)行set,在后續(xù)的任意需要記錄日志的方法里面進(jìn)行g(shù)et獲取到請求id,從而把整個(gè)請求串起來。
還有比如Spring的事務(wù)管理,用ThreadLocal存儲(chǔ)Connection,從而各個(gè)DAO可以獲取同一Connection,可以進(jìn)行事務(wù)回滾,提交等操作。
備注: ThreadLocal的這種用處,很多時(shí)候是用在一些優(yōu)秀的框架里面的,一般我們很少接觸,反而下面的場景我們接觸的更多一些!
線程安全的,避免某些情況需要考慮線程安全必須同步帶來的性能損失!??!
ThreadLocal為解決多線程程序的并發(fā)問題提供了一種新的思路。但是ThreadLocal也有局限性,我們來看看阿里規(guī)范:
每個(gè)線程往ThreadLocal中讀寫數(shù)據(jù)是線程隔離,互相之間不會(huì)影響的,所以ThreadLocal無法解決共享對象的更新問題!
由于不需要共享信息,自然就不存在競爭問題了,從而保證了某些情況下線程的安全,以及避免了某些情況需要考慮線程安全必須同步帶來的性能損失?。?!
這類場景阿里規(guī)范里面也提到了:
ThreadLocal一些細(xì)節(jié)!
ThreaLocal使用示例代碼:
public class ThreadLocalTest { private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { new Thread(() -> { try { for (int i = 0; i < 100; i++) { threadLocal.set(i); System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get()); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } finally { threadLocal.remove(); } }, "threadLocal1").start(); new Thread(() -> { try { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get()); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } finally { threadLocal.remove(); } }, "threadLocal2").start(); } }
代碼截圖:
代碼運(yùn)行結(jié)果:
從運(yùn)行的結(jié)果我們可以看到threadLocal1進(jìn)行set值對threadLocal2并沒有任何影響!
Thread、ThreadLocalMap、ThreadLocal總覽圖
Thread類有屬性變量threadLocals (類型是ThreadLocal.ThreadLocalMap),也就是說每個(gè)線程有一個(gè)自己的ThreadLocalMap ,所以每個(gè)線程往這個(gè)ThreadLocal中讀寫隔離的,并且是互相不會(huì)影響的。
一個(gè)ThreadLocal只能存儲(chǔ)一個(gè)Object對象,如果需要存儲(chǔ)多個(gè)Object對象那么就需要多個(gè)ThreadLocal?。?!
如圖:
看到上面的幾個(gè)圖,大概思路應(yīng)該都清晰了,我們Entry的key指向ThreadLocal用虛線表示弱引用 ,下面我們來看看ThreadLocalMap:
java對象的引用包括 : 強(qiáng)引用,軟引用,弱引用,虛引用 。
因?yàn)檫@里涉及到弱引用,簡單說明下:
弱引用也是用來描述非必需對象的,當(dāng)JVM進(jìn)行垃圾回收時(shí),無論內(nèi)存是否充足,該對象僅僅被弱引用關(guān)聯(lián),那么就會(huì)被回收。
當(dāng)僅僅只有ThreadLocalMap中的Entry的key指向ThreadLocal的時(shí)候,ThreadLocal會(huì)進(jìn)行回收的?。?!
ThreadLocal被垃圾回收后,在ThreadLocalMap里對應(yīng)的Entry的鍵值會(huì)變成null,但是Entry是強(qiáng)引用,那么Entry里面存儲(chǔ)的Object,并沒有辦法進(jìn)行回收,所以ThreadLocalMap 做了一些額外的回收工作。
雖然做了但是也會(huì)存在內(nèi)存泄漏風(fēng)險(xiǎn)(我沒有遇到過,網(wǎng)上很多類似場景,所以會(huì)提到后面的ThreadLocal最佳實(shí)踐?。。。?/p>
ThreadLocal的最佳實(shí)踐!
ThreadLocal被垃圾回收后,在ThreadLocalMap里對應(yīng)的Entry的鍵值會(huì)變成null,但是Entry是強(qiáng)引用,那么Entry里面存儲(chǔ)的Object,并沒有辦法進(jìn)行回收,所以ThreadLocalMap 做了一些額外的回收工作。
備注: 很多時(shí)候,我們都是用在線程池的場景,程序不停止,線程基本不會(huì)銷毀!?。?/p>
由于線程的生命周期很長,如果我們往ThreadLocal里面set了很大很大的Object對象,雖然set、get等等方法在特定的條件會(huì)調(diào)用進(jìn)行額外的清理,但是ThreadLocal被垃圾回收后,在ThreadLocalMap里對應(yīng)的Entry的鍵值會(huì)變成null,但是后續(xù)在也沒有操作set、get等方法了。
所以最佳實(shí)踐,應(yīng)該在我們不使用的時(shí)候,主動(dòng)調(diào)用remove方法進(jìn)行清理。
這里把ThreadLocal定義為static還有一個(gè)好處就是,由于ThreadLocal有強(qiáng)引用在,那么在ThreadLocalMap里對應(yīng)的Entry的鍵會(huì)永遠(yuǎn)存在,那么執(zhí)行remove的時(shí)候就可以正確進(jìn)行定位到并且刪除?。?!
最佳實(shí)踐做法應(yīng)該為:
try { // 其它業(yè)務(wù)邏輯 } finally { threadLocal對象.remove(); }
思考
如果面試的時(shí)候,可以把上面的內(nèi)容都可以講到,個(gè)人覺得就非常好了,回答的就挺完美了。但是如果你可以進(jìn)行下面的回答,那么就更完美了。
對于ThreadLocal,我在看Netty源碼的時(shí)候,還了解過FastThreadLocal,xxxxx一些列內(nèi)容,那就是一個(gè)升級了。
在我本地進(jìn)行測試,F(xiàn)astThreadLocal的吞吐量是jdkThreadLocal的3倍左右。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。