每個線程都有一個ThreadLocal線程本地變量,各個線程本地變量互不干擾。TreadLocalMap類型的變量(該類是一個輕量級的Map),可以調(diào)用set(),get()方法存取值,可以貫穿整個線程生命周期。鍵為當(dāng)前線程的id,值為Object類型。
作用:提供一個線程內(nèi)公共變量,減少同一個線程內(nèi)多個函數(shù)或者組件之間一些公共變量的傳遞的復(fù)雜度,讓線程的本地變量進(jìn)行隔離。
使用ThreadLocal示例:
創(chuàng)建ThreadLocal,定義threadlocal存儲值的類型,并且可以進(jìn)行初始化。通過threadLocal.get()獲取值,threadLocal.set(s)設(shè)置值。
public class ThreadLocalUse {
//創(chuàng)建threadLocal時并初始化
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Nullable
@Override
protected Integer initialValue() {
return 1;
}
};
public static void test() {
Thread[] threads = new Thread[3];
for (int i=0;i<threads.length;i++) {
threads[i] = new Thread(new TestThread(i), "thread" + i);
}
for (int i=0;i<threads.length;i++) {
threads[i].start();
}
}
public static class TestThread implements Runnable {
int id;
public TestThread(int id) {
this.id = id;
}
@Override
public void run() {
Integer s = threadLocal.get();
s = s + id;
threadLocal.set(s);
Log.i(TAG, Thread.currentThread().getName() + " threadLocal -> value: " + threadLocal.get());
}
}
}
調(diào)用ThreadLocalUse.test()啟動線程,打?。?/p>
thread0 threadLocal -> value: 1
thread1 threadLocal -> value: 2
thread2 threadLocal -> value: 3
3個threadLocal存儲初始值都是1,加上各自的id,分別為0,1,2,然后就打印出了1,2,3的各個值。3個ThreadLocal保存的值各不影響,各有3份副本。
不適用Threadlocal來存儲的示例:
public class UseThreadLocal {
static Integer threadLocal = new Integer(1);
public void start() {
Thread[] threads = new Thread[3];
for (int i=0;i<threads.length;i++) {
threads[i] = new Thread(new TestThread(i));
}
for (int i=0;i<threads.length;i++) {
threads[i].start();
}
}
public static class TestThread implements Runnable {
int id;
public TestThread(int id) {
this.id = id;
}
@Override
public void run() {
threadLocal = threadLocal + id;
System.out.println(Thread.currentThread().getName() + " threadLocal -> value: " + threadLocal);
}
}
}
打印的結(jié)果:
本來該打印1,2,3的,結(jié)果打印了1,2,4,數(shù)據(jù)就錯亂了。

源碼:
進(jìn)入ThreadLocal的get方法:
public T get() {
Thread t = Thread.currentThread();
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();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
獲取當(dāng)前線程然后調(diào)用了getMap(Thread t) 方法,調(diào)用了當(dāng)前線程的threadLocals。進(jìn)入之后,得到了ThreadLocalMap類型的成員變量,每個線程都有一個自己的ThreadLocalMap。
ThreadLocalMap類部分代碼:
static class ThreadLocalMap {
private static final int INITIAL_CAPACITY = 16;
private ThreadLocal.ThreadLocalMap.Entry[] table;
private int size;
private int threshold;
private void setThreshold(int var1) {
this.threshold = var1 * 2 / 3;
}
private static int nextIndex(int var0, int var1) {
return var0 + 1 < var1 ? var0 + 1 : 0;
}
private static int prevIndex(int var0, int var1) {
return var0 - 1 >= 0 ? var0 - 1 : var1 - 1;
}
ThreadLocalMap(ThreadLocal<?> var1, Object var2) {
this.size = 0;
this.table = new ThreadLocal.ThreadLocalMap.Entry[16];
int var3 = var1.threadLocalHashCode & 15;
this.table[var3] = new ThreadLocal.ThreadLocalMap.Entry(var1, var2);
this.size = 1;
this.setThreshold(16);
}
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> var1, Object var2) {
super(var1);
this.value = var2;
}
}
}
它有一個靜態(tài)內(nèi)部類Entry,保存一個value,數(shù)據(jù)存儲在Entry類型的數(shù)組中,獲取entry的value需要傳入threadLocal來作為key進(jìn)行獲取。
那么根據(jù)源碼畫出示意圖:

創(chuàng)建一個靜態(tài)的ThreadLocal變量,然后創(chuàng)建多個線程,通過threadLocal變量存和取數(shù)據(jù)。當(dāng)調(diào)用threadLocal.set時,判斷當(dāng)前線程的threadLocalMap是否為空,為空去new一個ThreadLocalMap對象,存入到當(dāng)前線程的threadLocals。不為空,則獲取threadLocalMap調(diào)用set方法,當(dāng)前ThreadLocal對象為鍵,存入值。當(dāng)調(diào)用threadLocal.get方法時,同樣先判斷當(dāng)前線程的threadLocalMap是否為空,不為空調(diào)用threadLocalMap的getEntry方法,使用當(dāng)前ThreadLocal對象為鍵,取出entry對象里面的value。這樣完成整個操作。
從圖中可以看到,每個線程的存取,都是對自己內(nèi)部唯一的threadLocalMap進(jìn)行操作,ThreadLocal對象只是作為了鍵來存取數(shù)據(jù),根本就沒有用到多線程的操作。
ThreadLocal引發(fā)的內(nèi)存泄漏
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> var1, Object var2) {
super(var1);
this.value = var2;
}
}

由于這個ThreadLocal使用weakreference來包裝的,所以在內(nèi)存不足時,threadlocal會被回收,而threadlocalMap在線程中是強(qiáng)引用,一旦threadlocal被回收,在threadlocalmap中對應(yīng)的value就無法再被訪問,就成了垃圾,且不可被回收,就發(fā)生了內(nèi)存泄漏。要解決這個問題,在用完了threadloacal之后,需要調(diào)用threadlocal的remove方法,將threadlocal所指向的value一起從內(nèi)存中清除。