標籤:thread 參數 new style 獨立 cte 指標 情況 oid
ThreadLocal 概念:
ThreadLocal不是用來解決對象共用訪問的問題,而主要是提供了儲存對象的方法和避免參數傳遞的方便的對象訪問方式。
ThreadLocal並不是一個Thread,而是Thread的局部變數,當使用ThreadLocal維護變數的時候ThreadLocal為每一個使用該變數的線程提供了獨立的變數副本,也就是說每一個線程都可以單獨改變自己的副本,而不會影響其他線程的副本。
從線程的角度來看目標變數就是當前線程的本地變數,這也就是類名Local的含義。
ThreadLocal的4個方法:
void set(T value) 將此線程局部變數的當前線程副本中的值設定為指定值
void remove() 移除此線程局部變數當前線程的值
protected T initialValue() 返回此線程局部變數的當前線程的初始值
T get() 返回此線程局部變數的當前線程副本中的值
注意 :預設情況下 initValue(), 返回 null 。線程在沒有調用 set 之前,第一次調用 get 的時候, get方法會預設去調用 initValue 這個方法。所以如果沒有覆寫這個方法,可能導致 get 返回的是 null 。當然如果調用過 set 就不會有這種情況了。但是往往在多線程情況下我們不能保證每個線程的在調用 get 之前都調用了set ,所以最好對 initValue 進行覆寫,以免導致null 指標異常。
ThreadLocal實現原理:
ThreadLocal是如何做到為每一個線程維護變數的副本呢?讓我們來看看ThreadLocal的源碼:
public class ThreadLocal<T> { public T get() { // 擷取當前線程 Thread t = Thread.currentThread(); // 擷取當前線程的threadLocals變數 ThreadLocalMap map = getMap(t); // 從當前線程的threadLocals變數中取得本threadLocal為key的值 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T) e.value; } return 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; } public void set(T value) { // 擷取當前線程 Thread t = Thread.currentThread(); // 擷取當前線程的threadLocals變數 ThreadLocalMap map = getMap(t); // 以本threadLocal為key的儲存值到當前線程的threadLocals變數中去 if (map != null) map.set(this, value); else createMap(t, value); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }}
在ThreadLocal類中有一個map,用於儲存沒一個線程的變數副本,map中的元素key為線程對象 value為線程副本中的變數值
樣本:
package cn.com.example;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * Created by Jack on 2017/2/13. */public class ThreadLocalTest { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 3; i++) { executorService.execute(new ThreadTest()); } executorService.shutdownNow(); }}class ThreadTest implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + " value=" + ThreadLocalNumber.get()); } }}class ThreadLocalNumber { private static ThreadLocal<Integer> value = new ThreadLocal<Integer>() { protected synchronized Integer initialValue() { return 0; } }; public static void set() { value.set(value.get() + 1); } public static int get() { // 為了測試 直接在get裡面 ++ value.set(value.get() + 1); return value.get(); }}
結果:
pool-1-thread-3 value=1pool-1-thread-2 value=1pool-1-thread-1 value=1pool-1-thread-2 value=2pool-1-thread-3 value=2pool-1-thread-2 value=3pool-1-thread-1 value=2pool-1-thread-2 value=4pool-1-thread-2 value=5pool-1-thread-3 value=3pool-1-thread-3 value=4pool-1-thread-3 value=5pool-1-thread-1 value=3pool-1-thread-1 value=4pool-1-thread-1 value=5
從運行結果得知 各線程都用於各自的Local變數,並各自讀寫互不干擾。
Java ThreadLocal 理解