我們知道Spring通過各種DAO模板類降低了研發(fā)者使用各種數(shù)據(jù)持久技術的難度。這些模板類都是線程安全的,也就是說,多個DAO能夠復用同一個模板實例而不會發(fā)生沖突。
我們使用模板類訪問底層數(shù)據(jù),根據(jù)持久化技術的不同,模板類需要綁定數(shù)據(jù)連接或會話的資源。但這些資源本身是非線程安全的,也就是說他們不能在同一時刻被多個線程共享。
雖然模板類通過資源池獲取數(shù)據(jù)連接或會話,但資源池本身解決的是數(shù)據(jù)連接或會話的緩存問題,并非數(shù)據(jù)連接或會話的線程安全問題。
按照傳統(tǒng)經(jīng)驗,假如某個對象是非線程安全的,在多線程環(huán)境下,對對象的訪問必須采用synchronized進行線程同步。但Spring的DAO模板類并未采用線程同步機制,因為線程同步限制了并發(fā)訪問,會帶來很大的性能損失。
此外,通過代碼同步解決性能安全問題挑戰(zhàn)性很大,可能會增強好幾倍的實現(xiàn)難度。那模板類究竟仰丈何種魔法神功,能夠在無需同步的情況下就化解線程安全的難題呢?答案就是ThreadLocal!
ThreadLocal在Spring中發(fā)揮著重要的作用,在管理request作用域的Bean、事務管理、任務調(diào)度、AOP等模塊都出現(xiàn)了他們的身影,起著舉足輕重的作用。要想了解Spring事務管理的底層技術,ThreadLocal是必須攻克的山頭堡壘。
ThreadLocal是什么
早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal為解決多線程程式的并發(fā)問題提供了一種新的思路。使用這個工具類能夠很簡潔地編寫出優(yōu)美的多線程程式。
ThreadLocal很容易讓人望文生義,想當然地認為是個“本地線程”。其實,ThreadLocal并不是個Thread,而是Thread的局部變量,也許把他命名為ThreadLocalVariable更容易讓人理解一些。
當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供單獨的變量副本,所以每一個線程都能夠單獨地改變自己的副本,而不會影響其他線程所對應的副本。
從線程的角度看,目標變量就象是線程的本地變量,這也是類名中“Local”所要表達的意思。
線程局部變量并不是Java的新發(fā)明,很多語言(如IBM IBM XL FORTRAN)在語法層面就提供線程局部變量。在Java中沒有提供在語言級支持,而是變相地通過ThreadLocal的類提供支持。
所以,在Java中編寫線程局部變量的代碼相對來說要笨拙一些,因此造成線程局部變量沒有在Java研發(fā)者中得到很好的普及。
ThreadLocal的接口方法
ThreadLocal類接口很簡單,只有4個方法,我們先來了解一下:
* void set(Object value)
配置當前線程的線程局部變量的值。
* public Object get()
該方法返回當前線程所對應的線程局部變量。
* public void remove()
將當前線程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束后,對應該線程的局部變量將自動被垃圾回收,所以顯式調(diào)用該方法清除線程的局部變量并不是必須的操作,但他能夠加快內(nèi)存回收的速度。
* protected Object initialValue()
返回該線程局部變量的初始值,該方法是個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是個延遲調(diào)用方法,在線程第1次調(diào)用get()或set(Object)時才執(zhí)行,并且僅執(zhí)行1次。ThreadLocal中的缺省實現(xiàn)直接返回一個null。