ThreadLocal 淺析

ThreadLocal是什么?

早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal為解決多線程程序的并發(fā)問題提供了一種新的思路。使用這個工具類可以很簡潔地編寫出優(yōu)美的多線程程序。
java.lang.ThreadLocal提供了線程局部 (thread-local) 變量。這些變量不同于它們的普通對應(yīng)物,因為訪問某個變量(通過其 get 或 set 方法)的每個線程都有自己的局部變量,它獨立于變量的初始化副本。ThreadLocal 實例通常是類中的 private static 字段,它們希望將狀態(tài)與某一個線程(例如,用戶 ID 或事務(wù) ID)相關(guān)聯(lián)。
每個線程都保持對其線程局部變量副本的隱式引用,只要線程是活動的并且 ThreadLocal 實例是可訪問的;在線程消失之后,其線程局部實例的所有副本都會被GC回收(除非存在對這些副本的其他引用)。

ThreadLocal接口方法

  • ThreadLocal對外提供了四個方法:
    • void set(T value) 設(shè)置當(dāng)前局部變量的值
   public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
  • product T initialValue() 返回該線程局部變量的初始值,該方法是一個protected的方法,方法內(nèi)部并沒有具體實現(xiàn)代碼,顯然是為了讓子類覆蓋而設(shè)計的。這個方法是一個延遲調(diào)用方法,在線程第1次調(diào)用get()或set(Object)時才執(zhí)行,并且僅執(zhí)行1次。但是如果在調(diào)用 get() 后又調(diào)用了 remove(),則可能再次調(diào)用此方法。
 protected T initialValue() {
       return null;
   }
  • public T get() 該方法返回當(dāng)前線程所對應(yīng)的線程局部變量。
       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();
   }
  • public void remove() 將當(dāng)前線程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK 5.0新增的方法。其實在線程結(jié)束后對應(yīng)的線程變量會自動被GC,主動調(diào)用這個方法目的是盡早回收,釋放內(nèi)存.
  public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

源碼淺析

也許我們分析JDK中的源碼有些難以理解,我們可以將ThreadLocal類簡化一下,簡化后的代碼如下

class MyThreadLocal<T>{
private Map<Thread,T> map = new HashMap<Thread,T>();
     protected T initialValue() {
        return null;
    }
  
  public void set(T value) {
        map.put(Thread.currentThread(), value);
    }
    
    public void remove() {
        map.remove(Thread.currentThread());
    }
    
    public T get() {
        return map.get(Thread.currentThread());
    }
}

其實在ThreadLocal內(nèi)部是維護了一個Map來保存數(shù)據(jù)的,雖然使用的時候獲取的是value值,但是其每一個value對應(yīng)的key都是當(dāng)前線程,因此相當(dāng)于將變量給每一個線程復(fù)制了一份,各個線程對數(shù)據(jù)的操作不會互相影響.

實用案例

我們在對數(shù)據(jù)庫進行操作的時候有時候為了數(shù)據(jù)安全必須加事務(wù)操作,但是一般事務(wù)管理在Service層,而數(shù)據(jù)操作在DAO層,一般的做法就是在Service層建立連接然后傳參的形式傳遞給DAO層,這無疑增加了程序的侵入性,其實在Spring的事務(wù)管理底層就使用了ThreadLocal來解決這種情況,我們也可以模擬使用ThreadLocal編寫一個帶有事務(wù)操作工具類.

public class JdbcUtils {
    private static DataSource dataSource = new ComboPooledDataSource();
    private static ThreadLocal<Connection> tl  = new ThreadLocal<Connection>();
  //獲取數(shù)據(jù)源
    public static DataSource getDataSource() {
        return dataSource;
    }
  //獲取連接
    public static Connection getConnection() throws SQLException {
        Connection con = tl.get(); 
        if(con == null) {
            return dataSource.getConnection();
        }
        return con;
    }
    //開始事務(wù)
    public static void beginTranscation() throws SQLException {
        Connection con = tl.get(); 
        if(con != null ) {
            throw new SQLException("事務(wù)已經(jīng)開啟,在沒有結(jié)束當(dāng)前事務(wù)時,不能再開啟事務(wù)!");
        }
        con = dataSource.getConnection(); 
        con.setAutoCommit(false); 
        tl.set(con); 
    }
    //提交事務(wù)
    public static void commitTransaction() throws SQLException {
        Connection con = tl.get(); 
        if(con == null ) {
            throw new SQLException("當(dāng)前沒有事務(wù),所以不能提交事務(wù)!");
        }
        con.commit(); 
        con.close(); 
        tl.remove(); 
    }
    // 回滾事務(wù)
    public static void rollbackTransaction() throws SQLException {
        Connection con = tl.get();
        if(con == null) {
            throw new SQLException("當(dāng)前沒有事務(wù),所以不能回滾事務(wù)!");
        }
        con.rollback();
        con.close();
        tl.remove();
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,798評論 11 349
  • 概述 ThreadLocal如果單純從名字上來看像是“本地線程"這么個意思,只能說這個名字起的確實不太好,很容易讓...
    eliter0609閱讀 537評論 0 0
  • 前言 ThreadLocal很多同學(xué)都搞不懂是什么東西,可以用來干嘛。但面試時卻又經(jīng)常問到,所以這次我和大家一起學(xué)...
    liangzzz閱讀 12,647評論 14 228
  • 122
    天空隨想閱讀 134評論 0 0
  • 小時候我有個夢想,學(xué)武。 想飛檐走壁,仗劍天涯。 二到拿著竹竿把草叢幻想成蝦兵蟹將一陣廝殺。 那種意淫的感覺爽爆了...
    夜暄閱讀 223評論 0 0

友情鏈接更多精彩內(nèi)容