理解java中的ThreadLocal
一、對(duì)ThreadLocal概術(shù)
JDK API 寫道:
該類提供了線程局部 (thread-local) 變量。這些變量不同于它們的普通對(duì)應(yīng)物,因?yàn)樵L問某個(gè)變量(通過其 get 或 set 方法)的每個(gè)線程都有自己的局部變量,它獨(dú)立于變量的初始化副本。ThreadLocal 實(shí)例通常是類中的 private static 字段,它們希望將狀態(tài)與某一個(gè)線程(例如,用戶 ID 或事務(wù) ID)相關(guān)聯(lián)。
二、結(jié)合源碼理解
可以看到ThreadLocal類中的變量只有這3個(gè)int型:








ThreadLocal實(shí)例的變量只有 threadLocalHashCode
ThreadLocal類的靜態(tài)變量nextHashCode 和HASH_INCREMENT
實(shí)際上HASH_INCREMENT是一個(gè)常量,表示了連續(xù)分配的兩個(gè)ThreadLocal實(shí)例的 threadLocalHashCode值的增量,而nextHashCode 的表示了即將分配的下一個(gè)ThreadLocal實(shí)例的threadLocalHashCode 的值。
而nextHashCode()方法就是將ThreadLocal類的下一個(gè)hashCode值即nextHashCode的值賦給實(shí)例的threadLocalHashCode,然后nextHashCode的值增加HASH_INCREMENT這個(gè)值。



ThreadLocal有一個(gè)ThreadLocalMap靜態(tài)內(nèi)部類,你可以簡單理解為一個(gè)MAP,這個(gè)‘Map’為每個(gè)線程復(fù)制一個(gè)變量的‘拷貝’存儲(chǔ)其中。
看一下set()方法: 獲取當(dāng)前線程的引用,從map中獲取該線程對(duì)應(yīng)的map,如果map存在更新緩存值,否則創(chuàng)建并存儲(chǔ)








再來看一下get()方法: 首先獲取當(dāng)前線程引用,以此為key去獲取響應(yīng)的ThreadLocalMap,如果此‘Map’不存在則初始化一個(gè),否則返回其中的變量。
調(diào)用get方法如果此Map不存在首先初始化,創(chuàng)建此map,將線程為key,初始化的vlaue存入其中,注意此處的initialValue,我們可以覆蓋此方法,在首次調(diào)用時(shí)初始化一個(gè)適當(dāng)?shù)闹?,默認(rèn)是null



























我們來看下ThreadLocalMap靜態(tài)內(nèi)部類,在ThreadLocalMap 內(nèi)部的Entry 是WeakReference













ThreadLocal和多線程并發(fā)沒有什么關(guān)系。ThreadLocal模式是為了解決單線程內(nèi)的跨類跨方法調(diào)用的
ThreadLocal不是用來解決對(duì)象共享訪問問題的,而主要是提供了保持對(duì)象的方法和避免參數(shù)傳遞的方便的對(duì)象訪問方式。一般情況下,通過ThreadLocal.set() 到線程中的對(duì)象是該線程自己使用的對(duì)象,其他線程是不需要訪問的,也訪問不到的。各個(gè)線程中訪問的是不同的對(duì)象。
三、例子
引用Tim Cull的博文“SimpleDateFormat: Performance Pig”介紹下ThreadLocal的簡單使用,同時(shí)也對(duì)SimpleDateFormat的使用有個(gè)深入的了解。
Tim Cull 寫道:
Tim Cull碰到一個(gè)SimpleDateFormat帶來的嚴(yán)重的性能問題,該問題主要有SimpleDateFormat引發(fā),創(chuàng)建一個(gè) SimpleDateFormat實(shí)例的開銷比較昂貴,解析字符串時(shí)間時(shí)頻繁創(chuàng)建生命周期短暫的實(shí)例導(dǎo)致性能低下。即使將 SimpleDateFormat定義為靜態(tài)類變量,貌似能解決這個(gè)問題,但是SimpleDateFormat是非線程安全的,同樣存在問題,如果用 ‘synchronized’線程同步同樣面臨問題,同步導(dǎo)致性能下降(線程之間序列化的獲取SimpleDateFormat實(shí)例)。
Tim Cull使用Threadlocal解決了此問題,對(duì)于每個(gè)線程SimpleDateFormat不存在影響他們之間協(xié)作的狀態(tài),為每個(gè)線程創(chuàng)建一個(gè)SimpleDateFormat變量的拷貝或者叫做副本
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 使用ThreadLocal以空間換時(shí)間解決SimpleDateFormat線程安全問題。
* @author
*
*/
public class DateUtil {
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
@SuppressWarnings("rawtypes")
private static ThreadLocal threadLocal = new ThreadLocal() {
protected synchronized Object initialValue() {
return new SimpleDateFormat(DATE_FORMAT);
}
};
public static DateFormat getDateFormat() {
return (DateFormat) threadLocal.get();
}
public static Date parse(String textDate) throws ParseException {
return getDateFormat().parse(textDate);
}
}




























創(chuàng)建一個(gè)ThreadLocal類變量,這里創(chuàng)建時(shí)用了一個(gè)匿名類,覆蓋了initialValue方法,主要作用是創(chuàng)建時(shí)初始化實(shí)例。也可以采用下面方式創(chuàng)建












posted on 2011-10-29 17:39 胡鵬 閱讀(1447) 評(píng)論(0) 編輯 收藏 所屬分類: java基礎(chǔ) 、J2EE