多線程中的ThreadLocal 詳解

要了解ThreadLocal,首先搞清楚ThreadLocal 是什麼?是用來解決什麼問題的?

ThreadLocal 是線程的局部變量, 是每一個線程所單獨持有的,其他線程不能對其進行訪問, 通常是類中的 private static 字段,是對該字段初始值的一個拷貝,它們希望將狀態與某一個線程(例如,用戶 ID 或事務 ID)相關聯

我們知道有時候一個對象的變量會被多個線程所訪問,這時就會有線程安全問題,當然我們可以使用synchorinized 關鍵字來爲此變量加鎖,進行同步處理,從而限制只能有一個線程來使用此變量,但是加鎖會大大影響程序執行效率,此外我們還可以使用ThreadLocal來解決對某一個變量的訪問衝突問題。

當使用ThreadLocal維護變量的時候 爲每一個使用該變量的線程提供一個獨立的變量副本,即每個線程內部都會有一個該變量,這樣同時多個線程訪問該變量並不會彼此相互影響,因此他們使用的都是自己從內存中拷貝過來的變量的副本, 這樣就不存在線程安全問題,也不會影響程序的執行性能。

但是要注意,雖然ThreadLocal能夠解決上面說的問題,但是由於在每個線程中都創建了副本,所以要考慮它對資源的消耗,比如內存的佔用會比不使用ThreadLocal要大

ThreadLocal 方法使用詳解

ThreadLocal 的幾個方法: ThreadLocal 可以存儲任何類型的變量對象, get返回的是一個Object對象,但是我們可以通過泛型來制定存儲對象的類型。

public T get() { } // 用來獲取ThreadLocal在當前線程中保存的變量副本
public void set(T value) { } //set()用來設置當前線程中變量的副本
public void remove() { } //remove()用來移除當前線程中變量的副本
protected T initialValue() { } //initialValue()是一個protected方法,一般是用來在使用時進行重寫的

Thread 在內部是通過ThreadLocalMap來維護ThreadLocal變量表, 在Thread類中有一個threadLocals 變量,是ThreadLocalMap類型的,它就是爲每一個線程來存儲自身的ThreadLocal變量的, ThreadLocalMap是ThreadLocal類的一個內部類,這個Map裏面的最小的存儲單位是一個Entry, 它使用ThreadLocal作爲key, 變量作爲 value,這是因爲在每一個線程裏面,可能存在着多個ThreadLocal變量

初始時,在Thread裏面,threadLocals爲空,當通過ThreadLocal變量調用get()方法或者set()方法,就會對Thread類中的threadLocals進行初始化,並且以當前ThreadLocal變量爲鍵值,以ThreadLocal要保存的副本變量爲value,存到threadLocals。 
然後在當前線程裏面,如果要使用副本變量,就可以通過get方法在threadLocals裏面查找

ThreadLocal是如何爲每一個線程創建一個變量副本的,下面舉一個例子來看一看。例子來源於 博客http://www.cnblogs.com/dolphin0520/p/3920407.html

public class ThreadLocalTest {
    public static void main(String[] args) throws InterruptedException {
        final ThreadLocalTest test = new ThreadLocalTest();

        test.set();
        System.out.println(test.getLong());
        System.out.println(test.getString());
        // 在這裏新建了一個線程
        Thread thread1 = new Thread() {
            public void run() {
                test.set(); // 當這裏調用了set方法,進一步調用了ThreadLocal的set方法是,會將ThreadLocal變量存儲到該線程的ThreadLocalMap類型的成員變量threadLocals中,注意的是這個threadLocals變量是Thread線程的一個變量,而不是ThreadLocal類的變量。
                System.out.println(test.getLong());
                System.out.println(test.getString());
            };
        };
        thread1.start();
        thread1.join();

        System.out.println(test.getLong());
        System.out.println(test.getString());
    }

    ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
    ThreadLocal<String> stringLocal = new ThreadLocal<String>();

    public void set() {
        longLocal.set(Thread.currentThread().getId());
        stringLocal.set(Thread.currentThread().getName());
    }

    public long getLong() {
        return longLocal.get();
    }

    public String getString() {
        return stringLocal.get();
    }

}
  •  

代碼的輸出結果: 

main 

Thread-0 

main

ThreadLocal 的應用場景

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

數據庫連接:

Class A implements Runnable{
    private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
        public Connection initialValue() {
                return DriverManager.getConnection(DB_URL);
        }
    };

    public static Connection getConnection() {
           return connectionHolder.get();
    }
}
  •  

Session管理

private static final ThreadLocal threadSession = new ThreadLocal();

public static Session getSession() throws InfrastructureException {
    Session s = (Session) threadSession.get();
    try {
        if (s == null) {
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
    } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
    }
    return s;
}
  •  

部分內容轉載自: 
http://www.cnblogs.com/dolphin0520/p/3920407.html

發佈了27 篇原創文章 · 獲贊 22 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章