這個(gè)實(shí)現(xiàn)的性能不會(huì)很好,因?yàn)槊總€(gè) get() 和 set() 操作都需要 values 映射表上的同步,而且如果多個(gè)線程同時(shí)訪問同一個(gè) ThreadLocal ,那么將發(fā)生爭(zhēng)用。此外,這個(gè)實(shí)現(xiàn)也是不切實(shí)際的,因?yàn)橛?Thread 對(duì)象做 values 映射表中的關(guān)鍵字將導(dǎo)致無法在線程退出后對(duì) Thread 進(jìn)行垃圾回收,而且也無法對(duì)死線程的 ThreadLocal 的特定于線程的值進(jìn)行垃圾回收。
get()
set()
values
ThreadLocal
Thread
用 ThreadLocal 實(shí)現(xiàn)每線程 Singleton
線程局部變量常被用來描繪有狀態(tài)“單子”(Singleton) 或線程安全的共享對(duì)象,或者是通過把不安全的整個(gè)變量封裝進(jìn) ThreadLocal ,或者是通過把對(duì)象的特定于線程的狀態(tài)封裝進(jìn) ThreadLocal 。例如,在與數(shù)據(jù)庫有緊密聯(lián)系的應(yīng)用程序中,程序的很多方法可能都需要訪問數(shù)據(jù)庫。在系統(tǒng)的每個(gè)方法中都包含一個(gè) Connection 作為參數(shù)是不方便的 — 用“單子”來訪問連接可能是一個(gè)雖然更粗糙,但卻方便得多的技術(shù)。然而,多個(gè)線程不能安全地共享一個(gè) JDBC Connection 。如清單 3 所示,通過使用“單子”中的 ThreadLocal ,我們就能讓我們的程序中的任何類容易地獲取每線程 Connection 的一個(gè)引用。這樣,我們可以認(rèn)為 ThreadLocal 允許我們創(chuàng)建 每線程單子。
Connection
任何創(chuàng)建的花費(fèi)比使用的花費(fèi)相對(duì)昂貴些的有狀態(tài)或非線程安全的對(duì)象,例如 JDBC Connection 或正則表達(dá)式匹配器,都是可以使用每線程單子(singleton)技術(shù)的好地方。當(dāng)然,在類似這樣的地方,您可以使用其它技術(shù),例如用池,來安全地管理共享訪問。然而,從可伸縮性角度看,即使是用池也存在一些潛在缺陷。因?yàn)槌貙?shí)現(xiàn)必須使用同步,以維護(hù)池?cái)?shù)據(jù)結(jié)構(gòu)的完整性,如果所有線程使用同一個(gè)池,那么在有很多線程頻繁地對(duì)池進(jìn)行訪問的系統(tǒng)中,程序性能將因爭(zhēng)用而降低。
用 ThreadLocal 簡(jiǎn)化調(diào)試日志紀(jì)錄
其它適合使用 ThreadLocal 但用池卻不能成為很好的替代技術(shù)的應(yīng)用程序包括存儲(chǔ)或累積每線程上下文信息以備稍后檢索之用這樣的應(yīng)用程序。例如,假設(shè)您想創(chuàng)建一個(gè)用于管理多線程應(yīng)用程序調(diào)試信息的工具。您可以用如清單 4 所示的 DebugLogger 類作為線程局部容器來累積調(diào)試信息。在一個(gè)工作單元的開頭,您清空容器,而當(dāng)一個(gè)錯(cuò)誤出現(xiàn)時(shí),您查詢?cè)撊萜饕詸z索這個(gè)工作單元迄今為止生成的所有調(diào)試信息。
DebugLogger
在您的代碼中,您可以調(diào)用 DebugLogger.put() 來保存您的程序正在做什么的信息,而且,稍后如果有必要(例如發(fā)生了一個(gè)錯(cuò)誤),您能夠容易地檢索與某個(gè)特定線程相關(guān)的調(diào)試信息。 與簡(jiǎn)單地把所有信息轉(zhuǎn)儲(chǔ)到一個(gè)日志文件,然后努力找出哪個(gè)日志記錄來自哪個(gè)線程(還要擔(dān)心線程爭(zhēng)用日志紀(jì)錄對(duì)象)相比,這種技術(shù)簡(jiǎn)便得多,也有效得多。
DebugLogger.put()
ThreadLocal 在基于 servlet 的應(yīng)用程序或工作單元是一個(gè)整體請(qǐng)求的任何多線程應(yīng)用程序服務(wù)器中也是很有用的,因?yàn)樵谔幚碚?qǐng)求的整個(gè)過程中將要用到單個(gè)線程。您可以通過前面講述的每線程單子技術(shù)用 ThreadLocal 變量來存儲(chǔ)各種每請(qǐng)求(per-request)上下文信息。