1、ThreadLocal簡介
ThreadLocal並非一個Thread,而是Thread的局部變量。
當使用ThreadLocal維護變量時,ThreadLocal爲每一個使用該變量的線程提供獨立的變量副本,因此每個線程均可以獨立地改變本身的副本,而不會影響其它線程所對應的副本。
2、爲何會出現ThreadLocal類
在java多線程併發訪問共享變量時,爲了線程安全,咱們的作法多是對多個線程訪問進行同步控制。可是這樣會下降系統的性能。在JDK 1.2的版本中就提供java.lang.ThreadLocal類,它爲每個線程都維護了一個變量,每一個線程都有了本身獨立的變量後,就沒有了併發下的線程安全問題。ThreadLocal類是以空間來換取線程安全的一種策略。
3、ThreadLocal原理
ThreadLocal爲每個線程維護變量的副本實現思路:
在ThreadLocal類中有一個Map,用於存儲每個線程的變量副本,Map中元素的鍵爲線程對象,而值對應線程的變量副本。
4、實現一個簡單的ThreadLocal
SimpleThreadLocal.java
public class SimpleThreadLocal {
private Map valueMap = Collections.synchronizedMap(new HashMap());
public void set(Object newValue) {
valueMap.put(Thread.currentThread(), newValue); //鍵爲線程對象,值爲本線程的變量副本
}
public Object get() {
Thread currentThread = Thread.currentThread();
Object o = valueMap.get(currentThread); //返回本線程對應的變量
if (o == null && !valueMap.containsKey(currentThread)) { //若是在Map中不存在,放到Map中保存起來。
o = initialValue();
valueMap.put(currentThread, o);
}
return o;
}
public void remove() {
valueMap.remove(Thread.currentThread());
}
public Object initialValue() {
return null;
}
}
5、API介紹
void set(Object value)
設置當前線程的線程局部變量的值;
public Object get()
該方法返回當前線程所對應的線程局部變量;
public void remove()
將當前線程局部變量的值刪除,目的是爲了減小內存的佔用,該方法是JDK 5.0新增的方法。須要指出的是,當線程結束後,對應該線程的局部變量將自動被垃圾回收,因此顯式調用該方法清除線程的局部變量並非必須的操做,但它能夠加快內存回收的速度;
protected Object initialValue()
返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是爲了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,而且僅執行1次。ThreadLocal中的默認實現直接返回一個null。
在JDK5.0中,ThreadLocal已經支持泛型,該類的類名已經變爲ThreadLocal<T>。API方法也相應進行了調整,新版本的API方法分別是void set(T value)、T get()以及T initialValue()。
6、ThreadLocal使用
看一個運用ThreadLocal來實現數據庫鏈接Connection對象線程隔離的例子
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ConnectionManager {
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
@Override
protected Connection initialValue() {
Connection conn = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "username","password");
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
public static void setConnection(Connection conn) {
connectionHolder.set(conn);
}
}
說明:經過調用ConnectionManager.getConnection()方法,每一個線程獲取到的,都是和當前線程綁定的那個Connection對象,第一次獲取時,是經過initialValue()方法的返回值來設置值的。經過ConnectionManager.setConnection(Connection conn)方法設置的Connection對象,也只會和當前線程綁定。這樣就實現了Connection對象在多個線程中的徹底隔離。在Spring容器中管理多線程環境下的Connection對象時,採用的思路和以上代碼很是類似。
7、線程同步和ThreadLocal的比較
對於多線程資源共享的問題,同步機制採用了「以時間換空間」的方式,而ThreadLocal採用了「以空間換時間」的方式。前者僅提供一份變量,讓不一樣的線程排隊訪問,然後者爲每個線程都提供了一份變量,所以能夠同時訪問而互不影響。 Synchronized用於線程間的數據共享,而ThreadLocal則用於線程間的數據隔離。 ThreadLocal並不能替代synchronized,它們處理不一樣的問題域。Synchronized用於實現同步機制,比ThreadLocal更加複雜。 參考資料: http://my.oschina.net/lichhao/blog/111362 http://stamen.iteye.com/blog/1535120