ThreadLocal源碼筆記

        最近用到了ThreadLocal,因此看了一下JDK的源碼。ThreadLocal是以空間換時間的典型,由於它避免了使用synchronized關鍵字來對變量加鎖,從而節約了不少時間。ThreadLocal是爲每一個線程建立一個變量的副本,因此每一個線程均可以訪問這個副本的內容並修改,彼此之間的副本不會相互影響,所以看似會修改同一個變量,但實際上線程間修改的都是線程本地變量。例以下面這個數據庫鏈接類:web

public class DatabaseUtil {
    private static final ThreadLocal<Connection> CON_HOLDER = new ThreadLocal<Connection>();

    public static Connection getConn() {
        Connection con = CON_HOLDER.get();
        if(null == con) {
            try {
                con = DriverManager.getConnection(url, username, pwd);
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                CON_HOLDER.set(con);
            }
        }
        return con;
    }

    // 其它函數
    ......
}

       咱們能夠在Servlet中調用getConn()方法去獲取一個鏈接,而後執行相應的數據庫操做(暫且忽略這種作法的不規範性,先討論多線程訪問的問題)。Servlet在Java web中是單實例多線程的執行模式,這也是爲何咱們通常不在Servlet類中定義成員變量的緣由,就是爲了不線程不安全。那麼當多個請求到來時,每一個請求都是在一個線程裏面來訪問servlet的方法的,所以看上去每一個線程獲取到的鏈接都是同一個實例(getConn方法相似單例模式)。但實際上並非這樣,每一個請求都會在它的線程中獲取到不一樣的Connection鏈接,這就是ThreadLocal的做用。首先看ThreadLocal的get()方法的源碼:數據庫

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

      首先它獲取了當前正在運行的線程,getMap()方法其實就是獲取Thread中的ThreadLocalMap對象,並無什麼特別的。而後從ThreadLocalMap中根據當前ThreadLocal對象做爲key去獲取對應的值,接着返回。Thread類中有如下兩個成員值得注意:安全

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

/*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

     剛纔的getMap()方法其實返回的就是threadLocal對象,如今Thread、ThreadLocal和ThreadLocalMap對象混在一塊兒有點亂,下面來理清楚它們的關係:多線程

     1. 每一個Thread都有一個ThreadLocalMap對象threadLocals做爲它的成員變量,它的key是ThreadLocal對象,值就是ThreadLocal中保存的值,若是一個線程訪問了多個ThreadLocal對象,則ThreadLocalMap中就會有多個映射關係函數

     2. 每個ThreadLocal對象都有本身惟一的hash值,這個值就是用於在ThreadLocalMap中查找對應的value的,因此以ThreadLocal對象爲key就能夠找到對應的線程本地的值this

     3. ThreadLocalMap是ThreadLocal的一個靜態內部類(就把它想象成一個普通的類就行了,只不過訪問它須要經過ThreadLocal來訪問),保存了不少個entry,每一個entry都是一個ThreadLocal與它的值的映射關係url

      說了這麼多,仍是貼一張圖比較直觀:線程

    從上圖能夠看到,每一個線程都擁有一個ThreadLocalMap對象,它會保存ThreadLocal和value的映射關係,ThreadLocal就至關於一箇中間人,每次線程方法它的get或set方法時,它都會首先獲取當前運行的線程,而後獲取線程中的ThreadLocalMap對象,因爲每一個ThreadLocal對象都有惟一hash值,因此以ThreadLocal爲key能夠在當前線程中找到它對應的值。每一個線程都維護本身的ThreadLocalMap對象,這個就是真正的「副本」,經過ThreadLocal這個中介就能夠保證成員變量的線程安全。每一個線程都有本身的映射(即ThreadLocalMap),當線程第一次設置ThreadLocal的值的時候,其實就是在本身的map中添加一條訪問的ThreadLocal對象和值的映射,建立副本,而後就能夠根據該ThreadLocal對象(也就是它的hashcode)找到對應的值,而不須要訪問共享的變量。code

    ThreadLocal沒有使用synchronized關鍵字,而是在Thread中維護一份本地的副本,若是每一個線程都但願獨享本身的成員變量時,ThreadLocal是一個好選擇,但若是確實須要多線程共享一個成員變量時,那麼仍是須要使用synchronized關鍵字來加鎖保證線程安全的。對象

相關文章
相關標籤/搜索