前言
在《Spring 3.x 企業(yè)應(yīng)用開發(fā)實(shí)戰(zhàn)》中我看到ThreadLocal這個類,當(dāng)時自己錯誤的認(rèn)為這個類可以解決并發(fā),還自己總結(jié)編發(fā)中的鎖是“時間換空間”,ThreadLocal類是“空間換時間”,當(dāng)我看了ThreadLocal的源碼后才發(fā)現(xiàn)自己的理解完全錯了。其實(shí)ThreadLocal不是用于解決共享變量的問題的,不是為了協(xié)調(diào)線程同步而存在,而是為了方便每個線程處理自己的狀態(tài)而引入的一個機(jī)制。
官方對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).
從中可以總結(jié)出三點(diǎn):
(1)每個線程都有自己的局部變量
(2)獨(dú)立于變量的初始化副本
(3)狀態(tài)與某一個線程相關(guān)聯(lián)
ThreadLocal例子
//例1
public class ThreadLocalTest {
//創(chuàng)建一個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() {
//獲取當(dāng)前線程的本地變量,然后累加5次
int num = local.get();
for (int i = 0; i < 5; i++) {
num++;
}
//重新設(shè)置累加后的本地變量
local.set(num);
System.out.println(Thread.currentThread().getName() + " : " + local.get());
}
}, "Thread-" + j);
}
for (Thread thread : threads) {
thread.start();
}
}
}
//執(zhí)行結(jié)果
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();
//創(chuàng)建一個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() {
//取出當(dāng)前線程的本地變量,并累加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++;
}
}
}
//執(zhí)行結(jié)果
Thread-0 : 11611
Thread-1 : 11613
Thread-2 : 22393
Thread-3 : 30219
Thread-4 : 40219
為什么兩例子會出現(xiàn)不同結(jié)果,根據(jù)代碼我們可以看出例1復(fù)制的是對象,例2復(fù)制的是對象的引用。
jdk老版本ThreadLocal的實(shí)現(xiàn)
《Spring 3.x 企業(yè)應(yīng)用開發(fā)實(shí)戰(zhàn)》中對ThreadLocal的簡單實(shí)現(xiàn),其實(shí)這個是jdk以前實(shí)現(xiàn)的思路,但是存在性能問題,現(xiàn)在已經(jīng)不這樣實(shí)現(xiàn),大家了解下。實(shí)現(xiàn)的思路很簡單:在ThreadLocal類中有一個Map,用于存儲每一個線程的變量副本,Map中元素的鍵為線程對象,而值對應(yīng)線程的變量副本。
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();
//②返回本線程對應(yīng)的變量
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的實(shí)現(xiàn)
源碼分析思路:ThreadLocal中set值,然后get取出值
set
set()方法
public void set(T value) {
//獲取當(dāng)前線程
Thread t = Thread.currentThread();
//獲取ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
//map存在在set(key,value),注意key是this,代表當(dāng)前ThreadLocal實(shí)例
map.set(this, value);
else
//不存在則創(chuàng)建,t是當(dāng)前線程
createMap(t, value);
}
createMap()方法
//ThreadLocalMap是ThreadLocal內(nèi)部類
void createMap(Thread t, T firstValue) {
//每個線程都有一個ThreadLocalMap
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap類
//構(gòu)造方法
//設(shè)置map的key值為threadLocal對象,value為參數(shù)中的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不存在則創(chuàng)建
createMap(t, value);
return value;
}
備注:在ThreadLocal的get()、set()、remove()的時候都會最終調(diào)用expungeStaleEntry()方法清除線程ThreadLocalMap里所有key為null的value。
ThreadLocal類中有個內(nèi)部類ThreadLocalMap,這個Map的key值是threadlocal實(shí)例,所以說ThreadLocal為線程局部變量。