我們知道Spring通過(guò)各種DAO模板類降低了研發(fā)者使用各種數(shù)據(jù)持久技術(shù)的難度。這些模板類都是線程安全的,也就是說(shuō),多個(gè)DAO能夠復(fù)用同一個(gè)模板實(shí)例而不會(huì)發(fā)生沖突。

我們使用模板類訪問(wèn)底層數(shù)據(jù),根據(jù)持久化技術(shù)的不同,模板類需要綁定數(shù)據(jù)連接或會(huì)話的資源。但這些資源本身是非線程安全的,也就是說(shuō)他們不能在同一時(shí)刻被多個(gè)線程共享。

雖然模板類通過(guò)資源池獲取數(shù)據(jù)連接或會(huì)話,但資源池本身解決的是數(shù)據(jù)連接或會(huì)話的緩存問(wèn)題,并非數(shù)據(jù)連接或會(huì)話的線程安全問(wèn)題。

按照傳統(tǒng)經(jīng)驗(yàn),假如某個(gè)對(duì)象是非線程安全的,在多線程環(huán)境下,對(duì)對(duì)象的訪問(wèn)必須采用synchronized進(jìn)行線程同步。但Spring的DAO模板類并未采用線程同步機(jī)制,因?yàn)榫€程同步限制了并發(fā)訪問(wèn),會(huì)帶來(lái)很大的性能損失。

此外,通過(guò)代碼同步解決性能安全問(wèn)題挑戰(zhàn)性很大,可能會(huì)增強(qiáng)好幾倍的實(shí)現(xiàn)難度。那模板類究竟仰丈何種魔法神功,能夠在無(wú)需同步的情況下就化解線程安全的難題呢?答案就是ThreadLocal!

ThreadLocal在Spring中發(fā)揮著重要的作用,在管理request作用域的Bean、事務(wù)管理、任務(wù)調(diào)度、AOP等模塊都出現(xiàn)了他們的身影,起著舉足輕重的作用。要想了解Spring事務(wù)管理的底層技術(shù),ThreadLocal是必須攻克的山頭堡壘。

ThreadLocal是什么

早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal為解決多線程程式的并發(fā)問(wèn)題提供了一種新的思路。使用這個(gè)工具類能夠很簡(jiǎn)潔地編寫出優(yōu)美的多線程程式。

ThreadLocal很容易讓人望文生義,想當(dāng)然地認(rèn)為是個(gè)“本地線程”。其實(shí),ThreadLocal并不是個(gè)Thread,而是Thread的局部變量,也許把他命名為ThreadLocalVariable更容易讓人理解一些。

當(dāng)使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線程提供單獨(dú)的變量副本,所以每一個(gè)線程都能夠單獨(dú)地改變自己的副本,而不會(huì)影響其他線程所對(duì)應(yīng)的副本。

從線程的角度看,目標(biāo)變量就象是線程的本地變量,這也是類名中“Local”所要表達(dá)的意思。

線程局部變量并不是Java的新發(fā)明,很多語(yǔ)言(如IBM IBM XL FORTRAN)在語(yǔ)法層面就提供線程局部變量。在Java中沒(méi)有提供在語(yǔ)言級(jí)支持,而是變相地通過(guò)ThreadLocal的類提供支持。

所以,在Java中編寫線程局部變量的代碼相對(duì)來(lái)說(shuō)要笨拙一些,因此造成線程局部變量沒(méi)有在Java研發(fā)者中得到很好的普及。

ThreadLocal的接口方法

ThreadLocal類接口很簡(jiǎn)單,只有4個(gè)方法,我們先來(lái)了解一下:

    * void set(Object value)

配置當(dāng)前線程的線程局部變量的值。

    * public Object get()

該方法返回當(dāng)前線程所對(duì)應(yīng)的線程局部變量。

    * public void remove()

將當(dāng)前線程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當(dāng)線程結(jié)束后,對(duì)應(yīng)該線程的局部變量將自動(dòng)被垃圾回收,所以顯式調(diào)用該方法清除線程的局部變量并不是必須的操作,但他能夠加快內(nèi)存回收的速度。

    * protected Object initialValue()

返回該線程局部變量的初始值,該方法是個(gè)protected的方法,顯然是為了讓子類覆蓋而設(shè)計(jì)的。這個(gè)方法是個(gè)延遲調(diào)用方法,在線程第1次調(diào)用get()或set(Object)時(shí)才執(zhí)行,并且僅執(zhí)行1次。ThreadLocal中的缺省實(shí)現(xiàn)直接返回一個(gè)null。