前言
ThreadLocal類位于java.lang包下,jdk1.2開始引入。ThreadLocal為每個使用該類變量的線程提供單獨的副本励饵,線程之間不會互相影響。就是說在A線程設置的值只能在A線程讀取到滑燃,在B線程是讀取不到的役听。
在多線程環(huán)境下,每個線程都有自己的數(shù)據(jù)表窘。一個線程使用自己的局部變量比使用全局變量好典予,因為局部變量只有線程自己能看見,不會影響其他線程乐严,而全局變量的修改必須加鎖瘤袖。
但是局部變量也有問題,就是在函數(shù)調(diào)用的時候昂验,傳遞起來很麻煩捂敌。
基本使用
這個類一共提供了四個方法。
-
set(T value)
將此線程局部變量的當前線程副本中的值設置為指定值既琴。 -
get()
返回此線程局部變量的當前線程副本中的值占婉。 -
remove()
移除此線程局部變量當前線程的值。 -
initialValue()
返回此線程局部變量的當前線程的“初始值”甫恩。該實現(xiàn)返回 null逆济;如果程序員希望線程局部變量具有null
以外的值,則必須為 ThreadLocal 創(chuàng)建子類磺箕,并重寫此方法奖慌。通常將使用匿名內(nèi)部類完成此操作。
下面看看ThreadLocal是如何解決這一問題的:
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ThreadLocalDemo {
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
public void test() {
threadLocal.set("hello");
printValue(); //打印 main -> hello
new Thread(new Runnable() {
@Override
public void run() {
printValue(); //打印 Thread-0 -> null
}
}).start();
}
private void printValue() {
System.out.println(Thread.currentThread().getName() + " -> " + threadLocal.get());
}
public static void main(String[] args) throws InterruptedException {
ThreadLocalDemo demo = new ThreadLocalDemo();
demo.test();
Thread.sleep(1000); //防止程序結(jié)束
}
}
從上面代碼中可以看到不同線程獲取到的值是不一樣的松靡,在主線程設置的值只能在主線程獲取到简僧,在子線程返回了一個null
。同時把ThreadLocal生明為全局變量击困,也不會影響各線程之間的值涎劈。
源碼分析
下面的代碼取自ThreadLocal類,展示了最基本的幾個方法阅茶。
//每個Thread都有自己的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//在不同線程調(diào)用這個方法獲取到的ThreadLocalMap對象是不一樣的蛛枚,
//所以從Map取到的對象更加不可能是一樣了。
if (map != null)
//用當前對象作為key存儲value
map.set(this, value);
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//用當前對象作為key獲取value
//由于是用當前對象作為key脸哀,所以一個ThreadLocal對象只能存儲一個值蹦浦。
//如果想要存儲幾個值可以多用幾個ThreadLocal。
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
//用當前對象作為key移除value
m.remove(this);
}
上面的代碼可以很容易看出ThreadLocal的原理撞蜂,但是由于ThreadLocal的代碼太多盲镶,這只是很小的一部分,為了便于大家理解問題蝌诡,我寫了一個類模擬ThreadLocal的實現(xiàn)溉贿。
public class ThreadLocal<T> {
//每個線程有單獨的Map來保證變量在不同線程之間互不影響
private static final Map<Thread, Map<ThreadLocal, Object>> tMap = new ConcurrentHashMap<>();
public void set(T value) {
Thread thread = Thread.currentThread();
Map<ThreadLocal, Object> map = tMap.get(thread);
if (map != null) {
map.put(this, value);
} else {
map = new ConcurrentHashMap<>();
tMap.put(thread, map);
map.put(this, value);
}
}
public T get() {
Thread thread = Thread.currentThread();
Map<ThreadLocal, Object> map = tMap.get(thread);
if (map != null) {
return (T) map.get(this);
}
return null;
}
public void remove() {
Thread thread = Thread.currentThread();
Map<ThreadLocal, Object> map = tMap.get(thread);
if (map != null) {
map.remove(this);
}
}
}
總結(jié)
每個Thread都有自己的ThreadLocalMap,存儲value的時候會存儲到自己的ThreadLocalMap浦旱,所以在哪個線程設置的value就只能在哪個線程獲取到宇色,其他Thread是獲取不到的。因為ThreadLocal類用this
當做key颁湖,所以每個ThreadLocal最多存儲一個value宣蠕。如果想要存儲幾個值可以多用幾個ThreadLocal。