初識ThreadLocal

今天來學習Java中的ThreadLocal,也叫做本地變量,主要有如下知識點:數據庫

  • 基本使用
  • 源碼解析
  • 使用場景

概述

ThreadLocal,不少地方叫作線程本地變量。
ThreadLocal在每一個線程中爲變量建立一個副本,即每一個線程內部都會有一個該變量,且在線程內部任何地方均可以使用,線程之間互不影響,這樣一來就不存在線程安全問題,也不會嚴重影響程序執行性能。安全

源碼解析

ThreadLocal類提供的幾個方法:多線程

public T get() {}
public void set(T value) {}
public void remove() {}
protected T initialValue() {}

get()方法是用來獲取ThreadLocal在當前線程中保存的變量副本.
set()用來設置當前線程中變量的副本.
remove()用來移除當前線程中變量的副本.
initialValue()是一個protected方法,通常是用來在使用時進行重寫.性能

首先咱們來看一下ThreadLocal類是如何爲每一個線程建立一個變量的副本的。學習

1.先看下get方法的實現:this

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(t)方法獲取到一個map,map的類型爲ThreadLocalMap
而後接着下面獲取到<key,value>鍵值對,注意這裏獲取鍵值對傳進去的是this,而不是當前線程t。
若是獲取成功,則返回value值。
若是map爲空,則調用setInitialValue方法返回value。線程

2.接着看一下getMap方法中作了什麼:code

ThreadLocalMap getMap(Thread t) {
   return t.threadLocals;
}

getMap中,是調用當前線程t,返回當前線程t中的一個成員變量threadLocals對象

3.那麼咱們繼續取Thread類中取看一下成員變量threadLocals是什麼:繼承

ThreadLocal.ThreadLocalMap threadLocals = null;

實際上就是一個ThreadLocalMap,這個類型是ThreadLocal類的一個內部類,咱們繼續取看ThreadLocalMap的實現:

static class ThreadLocalMap {

    static class Entry extends WeakReference<ThreadLocal> {
        Object value;

        Entry(ThreadLocal k, Object v) {
            super(k);
            value = v;
            
        }
    }
}

能夠看到ThreadLocalMap的Entry繼承了WeakReference,而且使用ThreadLocal做爲鍵值

4.而後再繼續看setInitialValue方法的具體實現:

private T setInitialValue() {
    
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
    
}

很容易瞭解,就是若是map不爲空,就設置鍵值對,爲空,再建立Map,看一下createMap的實現:

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

至此,可能大部分朋友已經明白了ThreadLocal是如何爲每一個線程建立變量的副本的:

首先,在 每一個線程Thread內部有一個ThreadLocal.ThreadLocalMap類型的成員變量threadLocals,這個threadLocals就是用來存儲實際的變量副本的,key值爲當前ThreadLocal變量,value爲變量副本(即T類型的變量)。
初始時,在Thread裏面,threadLocals爲空,當經過ThreadLocal變量調用get()方法或者set()方法,就會對Thread類中的threadLocals進行初始化,而且 以當前ThreadLocal變量爲鍵值,以ThreadLocal要保存的副本變量爲value,存到threadLocals。而後在當前線程裏面,若是要使用副本變量,就能夠經過get方法在threadLocals裏面查找。

總結

  • 每一個Thread維護着一個ThreadLocalMap的引用
  • ThreadLocalMap是ThreadLocal的內部類,用Entry來進行存儲
  • 調用ThreadLocal的set()方法時,實際上就是往ThreadLocalMap設置值,key是ThreadLocal對象,值是傳遞進來的對象
  • 調用ThreadLocal的get()方法時,實際上就是往ThreadLocalMap獲取值,key是ThreadLocal對象
  • ThreadLocal自己並不存儲值,它只是做爲一個key來讓線程從ThreadLocalMap獲取value

使用場景

最多見的ThreadLocal使用場景爲用來解決數據庫鏈接、Session管理等。

數據庫鏈接

private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>(){
    
    public Connection initialvalue () {
      return DriverManager.getConnection(DB_URL);
}
    public static Connection getConnection () {
      return connectionHolder.get();
   }
};

有關多線程的知識暫時先到這裏,後面有時間再補充!

相關文章
相關標籤/搜索