ThrealLocal詳解

ThrealLocal用於提供線程內部的局部變量,
這種變量在多線程環境下訪問能保證各個線程裏的變量相對獨立於其他線程內的變量,
也就是數據隔離

簡單例子


先看一個簡單示的ThreadLocal示例,創建五個線程,每個線程都會,
1. 獲取當前日期秒數
2. 存入一個變量中
3. 取出變量打印

final ThreadLocal<Long> secs = new ThreadLocal<Long>();

for(int i=0; i<5; i++) {

    try {
        Thread.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    new Thread(new Runnable() {
        public void run() {
            secs.set(System.currentTimeMillis());
            System.out.println(secs.get());
        }
    }).start();
}

如果對於變量不採取ThreadLocal控制,那麼每個線程對變量的讀寫過程將變得線程不安全了。
很多情況,都是對於DB數據庫的連接訪問會採取ThreadLocal處理。
如下代碼片段,

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

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

ThreadLocal的好處


  1. 數據隔離
  2. 簡化程序,易讀簡潔

ThreadLocal的用法


  • void set(Object value)
    設置當前線程的線程局部變量的值。
  • public Object get()
    該方法返回當前線程所對應的線程局部變量。
  • public void remove()
    將當前線程局部變量的值刪除,目的是爲了減少內存的佔用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束後,對應該線程的局部變量將自動被垃圾回收,所以顯式調用該方法清除線程的局部變量並不是必須的操作,但它可以加快內存回收的速度。
  • protected Object initialValue()
    返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是爲了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,並且僅執行1次。ThreadLocal中的缺省實現直接返回一個null。

ThreadLocal的原理


早期實現

聽說在JDK早期的代碼中,大概是這麼實現的,
1.在ThreadLocal中維護一個Map
2.當前的Thread作爲key,而要存儲的實例變量作爲value

如今實現

由於上述的那種方法會導致效率太低,所以出現了現在的實現方法
1.在Thread中維護一個Map
2.當前的ThreadLocal作爲key,而要存儲的實例變量作爲value

簡單類圖

這裏寫圖片描述

源碼解析

Thread.java類中的Map,用於保存ThreadLocal和實例變量:

ThreadLocal.ThreadLocalMap threadLocals = null;

tips:ThreadLocalMap是擴展於WeakReference的類,定義在ThreadLocal內部中。

那麼定義在線程中的ThreadLocalMap是如何初始化的呢?
初始化的過程在ThreadLocal.java中,會在調用set(Object value)方法的時候觸發:

 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

tips:這個方法會判斷當前線程的Map是否初始化,
如果有,賦值。
如果沒有,初始化,再賦值。

初始化的代碼如下,

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

tips:將當前線程中的Map初始化,再賦值

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章