首先先来看一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ThreadId {
private static final AtomicInteger nextId = new AtomicInteger(0);

private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};

public static int get() {
return threadId.get();
}

public static void main(String[] args) {
for(int i = 0; i < 10; i++) {
new Thread(new Runnable() {

@Override
public void run() {
System.out.println(get());
}
}).start();
}
}
}

然后控制台输出如下:

1
2
3
4
5
6
7
8
9
10
1
5
6
8
2
0
4
3
9
7

不难发现,不同的线程获取到的threadId是不一样的,那么为什么会出现这种情况呢,所以需要分析下ThreadLocal这个类,看看threadId.get()的逻辑

1
2
3
4
5
6
7
8
9
10
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}

第一步获取当前线程实例,然后调用getMap(t)获取到ThreadLocalMap实例

1
2
3
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

getMap(Thread t)方法直接返回了当前线程实例中的threadLocals成员变量,继续

1
ThreadLocal.ThreadLocalMap threadLocals = null;

其实threadLocalsThreadLocal中的静态内部类ThreadLocalMap类型,那么此时返回为null,接着调用return setInitialValue();

1
2
3
4
5
6
7
8
9
10
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;
}

此时调用了重写的initialValue()方法进行nextId的自增操作,接着调用createMap(t, value);

1
2
3
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

此时创建了一个ThreadLocalMap实例并复制给当前线程的threadLocals变量,再来看其创建过程

1
2
3
4
5
6
7
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);
}

这里创建了一个初始长度为INITIAL_CAPACITY即长度为 16 的Entry类型数组,然后通过firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);获取到数组下标,创建new Entry(firstKey, firstValue)赋值到tablei位置,size记录的是数组中被赋值的个数,setThreshold(INITIAL_CAPACITY);是计算threshold的大小,即INITIAL_CAPACITY * 2 / 3,当size达到这个值时,table数组将会被扩容。接着我们来看看Entry

1
2
3
4
5
6
7
8
9
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;

Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}

EntryThreadLocal的静态内部类,利用虚引用保存ThreadLocal实例,另外还保存了value值。
get方法的流程大致就是如此了,首先会获取当前类中的ThreadLocalMap类型的threadLocals成员变量,如果为空则调用初始化方法,并且创建一个ThreadLocalMap实例赋值给当前线程,然后经过取余计算获取对应数组的位置创建Entry实例并赋值。
ThreadLocalAndroid中也有应用,在Handler机制中,当我们调用Looper.prepare();创建Looper的时候用到了ThreadLocal存储。

1
2
3
4
5
6
7
8
9
10
public static void prepare() {
prepare(true);
}

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}

从代码中可以看出,一个线程最多只能创建一个Looper实例,不然会抛出异常,那么我们接下里就来分析下ThreadLocalset方法

1
2
3
4
5
6
7
8
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

逻辑和get方法一样先获取当前线程的threadLocals,如果为空和上面的流程一样,这里就不再讲了,接下来看看map.set(this, value);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private void set(ThreadLocal key, Object value) {

Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);

for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get();

if (k == key) {
e.value = value;
return;
}

if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}

tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}

大致就是先根据keyThreadLocal实例的threadLocalHashCode获取到需要赋值数组的位置,然后判断是否有实例存在,有则覆盖value值,没有则创建一个Entry实例赋值到对应位置,最后判断需不需要resize
ThreadLocal在我看来其实就是作用于线程范围的类,适用于不同的线程需要创建不同的数据副本情况,比如说Android中的Looper,暂时还未想到在Andriod实际开发中需要用到ThreadLocal的业务场景。