按照傳統(tǒng)的經(jīng)驗坝咐,如果某個對象是非線程安全的输玷,在多線程環(huán)境下對象的訪問需要采用synchronized進(jìn)行同步福也。但是模板類并未采用線程同步機制工育,因為線程同步會降低系統(tǒng)的并發(fā)性能,此外代碼同步解決線程安全問題的挑戰(zhàn)很大鹃愤,可能會增加好幾倍的實現(xiàn)難度簇搅。那么模板類到底采用什么方法來解決線程安全的難題呢?答案就是ThreadLocal软吐。
ThreadLocal是什么
顧名思義瘩将,ThreadLocal不是一個線程而是一個線程的本地化對象。當(dāng)工作于多線程環(huán)境中的對象采用ThreadLocal維護(hù)變量時凹耙,ThreadLocal為每個使用該變量的線程分配一個獨立的副本姿现。每個線程都可以獨立的改變自己的副本,而不影響其他線程的副本肖抱。
ThreadLocal的接口方法
void set(Object value) 設(shè)置當(dāng)前線程的線程局部變量的值
public Object get() 返回當(dāng)前線程的線程局部變量的值
public void remove() 刪除當(dāng)前線程的局部變量的值
protected Object initialValue() 返回當(dāng)前線程局部變量的初始值
那么ThreadLocal是如何做到為每一個線程維護(hù)一份獨立的變量副本的呢备典? 其實思路很簡單,在ThreadLocal類中有一個Map,Map中的鍵為線程對象意述,值為對應(yīng)線程的變量副本提佣。我們自己就可以實現(xiàn)一個簡單的版本如下:
一個ThreadLocal實例
ThreadLocal與線程同步機制的比較
線程同步機制通過對象的鎖機制保證同一時間只有一個線程去訪問變量,該變量時多個線程共享的荤崇。ThreadLocal則為每一個線程提供了一個變量副本拌屏,從而隔離了多個線程訪問數(shù)據(jù)的沖突,ThreadLocal提供了線程安全的對象封裝术荤,在編寫多線程代碼時倚喂,可以把不安全的代碼封裝進(jìn)ThreadLocal。概括的說,對于多線程資源共享的問題,線程同步機制采取了時間換空間的方式,訪問串行化,對象共享化揍异;而ThreadLocal采取了空間換時間的方式,訪問并行化,對象獨享化宙地。
Spring中采用ThreadLocal解決線程安全的問題
我們知道一般情況下,只有無狀態(tài)的bean才可以在多線程環(huán)境下共享喧兄,在spring中絕大多數(shù)的bean都可以聲明為singleton作用域无畔。就是因為spring對一些非線程安全的“狀態(tài)性對象”采用了ThreadLocal進(jìn)行封裝啊楚,讓它們成為線程安全的對象,因此有狀態(tài)的bean就可能以singleton的方式在多線程中正常工作了浑彰。
下面的實例能夠體現(xiàn)spring對有狀態(tài)bean的改造思路:
public class TopicDao {
private Connection conn;
public void addTopic(){
Statement stat = conn.createStatement();
...
}
}
上面代碼由于conn是非線程安全的成員變量恭理,因此addTopic方法是非線程安全的。下面使用ThreadLocal對該變量進(jìn)行改造郭变,使之變成線程安全的變量:
public class TopicDao {
private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();
private static Connection getConnection(){
if(connThreadLocal.get()==null){
Connection conn = ConnectionManager.getConnection();
connThreadLocal.set(conn);
return conn;
}else{
return connThreadLocal.get();
}
}
public void addTopic(){
Statement stat = getConnection.createStatement();
...
}
}