原文地址: http://www.iteye.com/topic/777716
ThreadLocal<T>類在Spring,Hibernate等框架中起到了很大的作用,對于其工作原理,很多網上的文章分析的不夠徹底,甚至有些誤解。
?
首先,為了解釋ThreadLocal類的工作原理,必須同時介紹與其工作甚密的其他幾個類(內部類)
1.ThreadLocalMap
2.Thread
可能有人會覺得Thread與ThreadLocal有什么關系,其實真正的奧秘就在Thread類中的一行:
?
- ThreadLocal.ThreadLocalMap?threadLocals?=? null ;??
?其中ThreadLocalMap的定義是在ThreadLocal類中,真正的引用卻是在Thread類中
?
那么ThreadLocalMap究竟是什么呢?
?
可以看到這個類應該是一個Map,JDK的解釋是
?
?
?
接下來的重點是ThreadLocalMap中用于存儲數據的entry
?
- static ? class ?Entry? extends ?WeakReference<ThreadLocal>?{ ??
- ???????????? /**?The?value?associated?with?this?ThreadLocal.?*/ ??
- ????????????Object?value; ??
- ??
- ????????????Entry(ThreadLocal?k,?Object?v)?{ ??
- ???????????????? super (k); ??
- ????????????????value?=?v; ??
- ????????????} ??
- ????????}??
?
?從中我們可以發現 這個Map的key是ThreadLocal變量,value為用戶的值,并不是網上大多數的列子key是線程的名字或者標識
?
到這里,我們就可以理解ThreadLocal究竟是如何工作的了
?
1.Thread類中有一個成員變量叫做ThreadLocalMap,它是一個Map,他的Key是ThreadLocal類
2 .每個線程擁有自己的申明為ThreadLocal類型的變量,所以這個類的名字叫'ThreadLocal':線程自己的(變量)
3.此變量生命周期是由該線程決定的,開始于第一次初始(get或者set方法)
4.由ThreadLocal的工作原理決定了: 每個線程獨自擁有一個變量,并非共享或者拷貝
?
- /** ?
- ?*?@author?mxdba ?
- ?* ?
- ?*/ ??
- public ? class ?ThreadLocalSample?{ ??
- ??
- ???? public ? static ? void ?main(String[]?args)?{ ??
- ????????ThreadTest?test1?=? new ?ThreadTest( 10 ); ??
- ????????ThreadTest?test2?=? new ?ThreadTest( 20 ); ??
- ????????test1.start(); ??
- ????????test2.start(); ??
- ????} ??
- ??
- } ??
- ??
- /** ?
- ?*?此線程有兩個ThreadLocal變量,但是由于ThreadLocal是延遲初始的, ?
- ?*?所以在debug時可以看到線程名為“線程20”的線程的ThreadLocalMap中沒有thLcal2這個entry ?
- ?*?@author?mxdba ?
- ?*? ?
- ?*/ ??
- class ?ThreadTest? extends ?Thread?{ ??
- ???? ??
- ???? public ? static ?ThreadLocal<Integer>?thLocal?=? new ?ThreadLocal<Integer>(); ??
- ???? public ? static ?ThreadLocal<String>?thLocal2?=? new ?ThreadLocal<String>(); ??
- ???? ??
- ???? public ?Integer?num; ??
- ???? ??
- ???? ??
- ???? ??
- ???? public ?ThreadTest(Integer?num)?{ ??
- ???????? super ( "線程" ?+?num); ??
- ???????? this .num?=?num; ??
- ????} ??
- ??
- ???? @Override ??
- ???? public ? void ?run()?{ ??
- ????????Integer?n?=?thLocal.get(); ??
- ???????? if (num?!=? 20 )?{ ??
- ????????????String?s?=?thLocal2.get(); ??
- ????????} ??
- ???????????? ??
- ???????? if (n?==? null )?{ ??
- ????????????thLocal.set(num); ??
- ????????} ??
- ????????System.out.println(thLocal.get()); ??
- ????} ??
- ???? ??
- }??
?
?
接下來分析一下源碼,就更加清楚了
?
- /** ??
- ?*?關鍵方法,返回當前Thread的ThreadLocalMap ??
- ?*?[[[每個Thread返回各自的ThreadLocalMap,所以各個線程中的ThreadLocal均為獨立的]]] ??
- ?*/ ??
- ThreadLocalMap?getMap(Thread?t)?{ ??
- ????????return?t.threadLocals; ??
- ????}??
?
?
?
?
- public?T?get()?{ ??
- ????????Thread?t?=?Thread.currentThread(); ??
- ????????/** ??
- ?????????*?得到當前線程的ThreadLocalMap ??
- ?????????*/ ??
- ????????ThreadLocalMap?map?=?getMap(t); ??
- ????????if?(map?!=?null)?{ ??
- ????????????/** ??
- ?????????????*?在此線程的ThreadLocalMap中查找key為當前ThreadLocal對象的entry ??
- ?????????????*/ ??
- ????????????ThreadLocalMap.Entry?e?=?map.getEntry(this); ??
- ????????????if?(e?!=?null) ??
- ????????????????return?(T)e.value; ??
- ????????} ??
- ????????return?setInitialValue(); ??
- ????}??
?
?
?
?
- private?T?setInitialValue()?{ ??
- ????????/** ??
- ?????????*?默認返回null,這個方法為protected可以繼承 ??
- ?????????*/ ??
- ????????T?value?=?initialValue(); ??
- ????????Thread?t?=?Thread.currentThread(); ??
- ????????ThreadLocalMap?map?=?getMap(t); ??
- ????????if?(map?!=?null) ??
- ????????????map.set(this,?value); ??
- ????????else ??
- ????????????/** ??
- ?????????????*?初次創建 ??
- ?????????????*/ ??
- ????????????createMap(t,?value); ??
- ????????return?value; ??
- ????}??
?
?
?
- /** ?
- ?*?給當前thread初始ThreadlocalMap ?
- ?*/ ??
- void ?createMap(Thread?t,?T?firstValue)?{ ??
- ????????t.threadLocals?=? new ?ThreadLocalMap( this ,?firstValue); ??
- ????}??
?
?
?
通過上邊的分析,我們發現,ThreadLocal類的使用雖然是用來解決多線程的問題的,但是還是有很明顯的針對性
1. 最明顯的,ThreadLoacl變量的活動范圍為某線程,并且我的理解是該線程“專有的,獨自霸占”,對該變量的所有操作均有該線程完成! 也就是說,ThreadLocal不是用來解決共享,競爭問題的。典型的應用莫過于Spring,Hibernate等框架中對于多線程的處理了
?
- private ? static ? final ?ThreadLocal?threadSession?=? new ?ThreadLocal();?? ??
- ?? ??
- public ? static ?Session?getSession()? throws ?InfrastructureException?{?? ??
- ????Session?s?=?(Session)?threadSession.get();?? ??
- ???? try ?{?? ??
- ???????? if ?(s?==? null )?{?? ??
- ????????????s?=?getSessionFactory().openSession();?? ??
- ????????????threadSession.set(s);?? ??
- ????????}?? ??
- ????}? catch ?(HibernateException?ex)?{?? ??
- ???????? throw ? new ?InfrastructureException(ex);?? ??
- ????}?? ??
- ???? return ?s;?? ??
- }????
?這段代碼,每個線程有自己的ThreadLocalMap,每個ThreadLocalMap中根據需要初始加載threadSession,這樣的好處就是介于singleton與prototype之間,應用singleton無法解決線程,應用prototype開銷又太大,有了ThreadLocal之后就好了,對于需要線程“霸占”的變量用ThreadLocal,而該類實例的方法均可以共享。
?
2.關于內存泄漏:
雖然ThreadLocalMap已經使用了weakReference,但是還是建議能夠顯示的使用remove方法。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
