Java線程中的線程本地變量ThreadLocal詳解
ThreadLocal
首先說(shuō)明ThreadLocal存放的值是線程內(nèi)共享的,線程間互斥的,主要用于線程內(nèi)共享一些數(shù)據(jù),避免通過(guò)參數(shù)來(lái)傳遞,這樣處理后,能夠優(yōu)雅的解決一些實(shí)際問(wèn)題,比如Hibernate中的OpenSessionInView,就是使用ThreadLocal保存Session對(duì)象,還有我們經(jīng)常用ThreadLocal存放Connection,代碼如:
/** * 數(shù)據(jù)庫(kù)連接管理類 * @author 爽 * */ public class ConnectionManager { /** 線程內(nèi)共享Connection,ThreadLocal通常是全局的,支持泛型 */ private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); public static Connection getCurrConnection() { // 獲取當(dāng)前線程內(nèi)共享的Connection Connection conn = threadLocal.get(); try { // 判斷連接是否可用 if(conn == null || conn.isClosed()) { // 創(chuàng)建新的Connection賦值給conn(略) // 保存Connection threadLocal.set(conn); } } catch (SQLException e) { // 異常處理 } return conn; } /** * 關(guān)閉當(dāng)前數(shù)據(jù)庫(kù)連接 */ public static void close() { // 獲取當(dāng)前線程內(nèi)共享的Connection Connection conn = threadLocal.get(); try { // 判斷是否已經(jīng)關(guān)閉 if(conn != null && !conn.isClosed()) { // 關(guān)閉資源 conn.close(); // 移除Connection threadLocal.remove(); conn = null; } } catch (SQLException e) { // 異常處理 } } }
這樣處理的好處:
- 統(tǒng)一管理Connection;
- 不需要顯示傳參Connection,代碼更優(yōu)雅;
- 降低耦合性。
ThreadLocal有四個(gè)方法
initialValue
protected T initialValue()
返回此線程局部變量的當(dāng)前線程的初始值。最多在每次訪問(wèn)線程來(lái)獲得每個(gè)線程局部變量時(shí)調(diào)用此方法一次,即線程第一次使用 get() 方法訪問(wèn)變量的時(shí)候。如果線程先于 get 方法調(diào)用 set(T) 方法,則不會(huì)在線程中再調(diào)用 initialValue 方法。
該實(shí)現(xiàn)只返回 null;如果程序員希望將線程局部變量初始化為 null 以外的某個(gè)值,則必須為 ThreadLocal 創(chuàng)建子類,并重寫此方法。通常,將使用匿名內(nèi)部類。initialValue 的典型實(shí)現(xiàn)將調(diào)用一個(gè)適當(dāng)?shù)臉?gòu)造方法,并返回新構(gòu)造的對(duì)象。
返回:
返回此線程局部變量的初始值
get
public T get()
返回此線程局部變量的當(dāng)前線程副本中的值。如果這是線程第一次調(diào)用該方法,則創(chuàng)建并初始化此副本。
返回:
此線程局部變量的當(dāng)前線程的值
set
public void set(T value)
將此線程局部變量的當(dāng)前線程副本中的值設(shè)置為指定值。許多應(yīng)用程序不需要這項(xiàng)功能,它們只依賴于 initialValue() 方法來(lái)設(shè)置線程局部變量的值。
參數(shù):
value - 存儲(chǔ)在此線程局部變量的當(dāng)前線程副本中的值。
remove
public void remove()
移除此線程局部變量的值。這可能有助于減少線程局部變量的存儲(chǔ)需求。如果再次訪問(wèn)此線程局部變量,那么在默認(rèn)情況下它將擁有其 initialValue。
很多人對(duì)ThreadLocal存在一定的誤解,說(shuō)ThreadLocal中有一個(gè)全局的Map,set時(shí)執(zhí)行map.put(Thread.currentThread(), value),get和remove時(shí)也同理,但SUN的大師們是否是如此實(shí)現(xiàn)的,我們只能去看源碼了。
源碼解讀
set方法
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ public void set(T value) { // 獲取當(dāng)前線程對(duì)象 Thread t = Thread.currentThread(); // 獲取當(dāng)前線程本地變量Map ThreadLocalMap map = getMap(t); // map不為空 if (map != null) // 存值 map.set(this, value); else // 創(chuàng)建一個(gè)當(dāng)前線程本地變量Map createMap(t, value); } /** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t) { // 獲取當(dāng)前線程的本地變量Map return t.threadLocals; }
這里注意,ThreadLocal中是有一個(gè)Map,但這個(gè)Map不是我們平時(shí)使用的Map,而是ThreadLocalMap,ThreadLocalMap是ThreadLocal的一個(gè)內(nèi)部類,不對(duì)外使用的。當(dāng)使用ThreadLocal存值時(shí),首先是獲取到當(dāng)前線程對(duì)象,然后獲取到當(dāng)前線程本地變量Map,最后將當(dāng)前使用的ThreadLocal和傳入的值放到Map中,也就是說(shuō)ThreadLocalMap中存的值是[ThreadLocal對(duì)象, 存放的值],這樣做的好處是,每個(gè)線程都對(duì)應(yīng)一個(gè)本地變量的Map,所以一個(gè)線程可以存在多個(gè)線程本地變量。
get方法
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } // 如果值為空,則返回初始值 return setInitialValue(); }
有了之前set方法的分析,get方法也同理,需要說(shuō)明的是,如果沒(méi)有進(jìn)行過(guò)set操作,那從ThreadLocalMap中拿到的值就是null,這時(shí)get方法會(huì)返回初始值,也就是調(diào)用initialValue()方法,ThreadLocal中這個(gè)方法默認(rèn)返回null。當(dāng)我們有需要第一次get時(shí)就能得到一個(gè)值時(shí),可以繼承ThreadLocal,并且覆蓋initialValue()方法。
到此這篇關(guān)于Java線程中的線程本地變量ThreadLocal詳解的文章就介紹到這了,更多相關(guān)Java線程本地變量ThreadLocal內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java使用hadoop實(shí)現(xiàn)關(guān)聯(lián)商品統(tǒng)計(jì)
本篇文章java使用hadoop實(shí)現(xiàn)關(guān)聯(lián)商品統(tǒng)計(jì),可以實(shí)現(xiàn)商品的關(guān)聯(lián)統(tǒng)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-10-10SpringBoot條件注解之@ConditionalOnClass等注解的使用場(chǎng)景分析
文章詳細(xì)介紹了SpringBoot中條件注解的體系,包括基本概念、@ConditionalOnClass等常用注解的工作原理和使用場(chǎng)景,文章還探討了條件注解的組合使用、實(shí)戰(zhàn)應(yīng)用以及最佳實(shí)踐,幫助開(kāi)發(fā)者更好地理解和應(yīng)用條件注解,實(shí)現(xiàn)更靈活和智能的應(yīng)用配置,感興趣的朋友一起看看吧2025-03-03簡(jiǎn)單剖析Java中動(dòng)態(tài)線程池的擴(kuò)容以及縮容操作
這篇文章主要為大家詳細(xì)介紹了Java中動(dòng)態(tài)線程池的擴(kuò)容以及縮容操作的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-01-01Java對(duì)象創(chuàng)建內(nèi)存案例解析
這篇文章主要介紹了Java對(duì)象創(chuàng)建內(nèi)存案例解析,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08關(guān)于idea-web.xml版本過(guò)低怎么生成新的(web.xml報(bào)錯(cuò))問(wèn)題
今天通過(guò)本文給大家分享idea-web.xml版本過(guò)低怎么生成新的(web.xml報(bào)錯(cuò))問(wèn)題,通過(guò)更換web.xml版本解決此問(wèn)題,感興趣的朋友跟隨小編一起看看吧2021-07-07kafka springBoot配置的實(shí)現(xiàn)
本文主要介紹了kafka springBoot配置的實(shí)現(xiàn),通過(guò)詳細(xì)解析Spring Boot for Apache Kafka的配置選項(xiàng),以及如何優(yōu)化Kafka生產(chǎn)者和消費(fèi)者的屬性設(shè)置,感興趣的可以了解一下2023-11-11淺談java中String StringBuffer StringBuilder的區(qū)別
下面小編就為大家?guī)?lái)一篇淺談java中String StringBuffer StringBuilder的區(qū)別。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-06-06