前言
在《Spring 3.x 企業應用開發實戰》中我看到ThreadLocal這個類,當時自己錯誤的認為這個類可以解決并發,還自己總結編發中的鎖是“時間換空間”,ThreadLocal類是“空間換時間”,當我看了ThreadLocal的源碼后才發現自己的理解完全錯了。其實ThreadLocal不是用于解決共享變量的問題的,不是為了協調線程同步而存在,而是為了方便每個線程處理自己的狀態而引入的一個機制。
官方對ThreadLocal的描述
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
從中可以總結出三點:
(1)每個線程都有自己的局部變量
(2)獨立于變量的初始化副本
(3)狀態與某一個線程相關聯
ThreadLocal例子
//例1
public class ThreadLocalTest {
//創建一個Integer型的線程本地變量
public static final ThreadLocal<Integer> local = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[5];
for (int j = 0; j < 5; j++) {
threads[j] = new Thread(new Runnable() {
public void run() {
//獲取當前線程的本地變量,然后累加5次
int num = local.get();
for (int i = 0; i < 5; i++) {
num++;
}
//重新設置累加后的本地變量
local.set(num);
System.out.println(Thread.currentThread().getName() + " : " + local.get());
}
}, "Thread-" + j);
}
for (Thread thread : threads) {
thread.start();
}
}
}
//執行結果
Thread-0 : 5
Thread-1 : 5
Thread-2 : 5
Thread-3 : 5
Thread-4 : 5
//例2
public class ThreadLocalTest {
private static Index num = new Index();
//創建一個Index類型的本地變量
private static ThreadLocal<Index> local = new ThreadLocal<Index>() {
@Override
protected Index initialValue() {
return num;
}
};
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[5];
for (int j = 0; j < 5; j++) {
threads[j] = new Thread(new Runnable() {
public void run() {
//取出當前線程的本地變量,并累加10000次
Index index = local.get();
for (int i = 0; i < 10000; i++) {
index.increase();
}
System.out.println(Thread.currentThread().getName() + " : "+ index.num);
}
}, "Thread-" + j);
}
for (Thread thread : threads) {
thread.start();
}
}
static class Index {
int num;
public void increase() {
num++;
}
}
}
//執行結果
Thread-0 : 11611
Thread-1 : 11613
Thread-2 : 22393
Thread-3 : 30219
Thread-4 : 40219
為什么兩例子會出現不同結果,根據代碼我們可以看出例1復制的是對象,例2復制的是對象的引用。
jdk老版本ThreadLocal的實現
《Spring 3.x 企業應用開發實戰》中對ThreadLocal的簡單實現,其實這個是jdk以前實現的思路,但是存在性能問題,現在已經不這樣實現,大家了解下。實現的思路很簡單:在ThreadLocal類中有一個Map,用于存儲每一個線程的變量副本,Map中元素的鍵為線程對象,而值對應線程的變量副本。
public class SimpleThreadLocal {
private Map valueMap = Collections.synchronizedMap(new HashMap());
public void set(Object newValue) {
//①鍵為線程對象,值為本線程的變量副本
valueMap.put(Thread.currentThread(), newValue);
}
public Object get() {
Thread currentThread = Thread.currentThread();
//②返回本線程對應的變量
Object o = valueMap.get(currentThread);
//③如果在Map中不存在,放到Map中保存起來
if (o == null && !valueMap.containsKey(currentThread)) {
o = initialValue();
valueMap.put(currentThread, o);
}
return o;
}
public void remove() {
valueMap.remove(Thread.currentThread());
}
public Object initialValue() {
return null;
}
}
jdk1.7版本ThreadLocal的實現
源碼分析思路:ThreadLocal中set值,然后get取出值
set
set()方法
public void set(T value) {
//獲取當前線程
Thread t = Thread.currentThread();
//獲取ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//map存在在set(key,value),注意key是this,代表當前ThreadLocal實例
map.set(this, value);
else
//不存在則創建,t是當前線程
createMap(t, value);
}
createMap()方法
//ThreadLocalMap是ThreadLocal內部類
void createMap(Thread t, T firstValue) {
//每個線程都有一個ThreadLocalMap
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap類
//構造方法
//設置map的key值為threadLocal對象,value為參數中的object。
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
//對ThreadLocal軟引用
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
get()
public T get() {
Thread t = Thread.currentThread();
//獲得ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
//存在則獲取值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//不存在則初始化值
return setInitialValue();
}
//初始化值
private T setInitialValue() {
//初始化為null
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
//set值
map.set(this, value);
else
//map不存在則創建
createMap(t, value);
return value;
}
備注:在ThreadLocal的get()、set()、remove()的時候都會最終調用expungeStaleEntry()方法清除線程ThreadLocalMap里所有key為null的value。
ThreadLocal類中有個內部類ThreadLocalMap,這個Map的key值是threadlocal實例,所以說ThreadLocal為線程局部變量。