看一个案例:
public static void main(String[] args) {
ThreadLocal<String> own = new ThreadLocal<String>();
own.set(Thread.currentThread().getName());
System.out.println(own.get());
Thread thread = new Thread(()->{
own.set(Thread.currentThread().getName());
System.out.println(own.get());
}, "other thread");
thread.start();
}
输出:
main
other thread
ThreadLocal的作用显而易见:
某变量是线程共享变量,但某线程下对其的操作仅对该线程可见
看下ThreadLocal.set()方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
拿到当前线程的实例,从线程实例中获取Map,若map实例非空则赋值;若无,则创建新的map并赋值。
看下map.set()的方法:
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
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();
}
如上的代码大概意思就是将ThreadLocal 变量作为 Entry的 key, 实际值作为Entry的value,该Entry是保存在table[]中的。这里要注意,Entry,table[]均是ThreadLocalMap的内部数据结构,该ThreadLocalMap是Thread独有的。
还有一点需要注意:Entry继承了WeakReference, 并将TnreadLocal变量设置为弱引用,为什么呢,这是因为线程有一种不被回收的情况(线程池中的线程),那Thread实例就会一直存在,所以其持有的ThreadLocalMap就会一直存在,但map里的Entry中的ThreadLocal(该引用在外部是强引用)假如也是强引用,那即使外部引用消失它也不会被GC,所以设置为WeakReference,当外部不再引用时其可被GC。
再看一下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();
}
有了set()的分析,不难解析get()的源代码
先获取当前线程的实例,然后再获取该线程实例中的ThreadLocalMap,再根据ThreadLocal变量在table[]中获取Entry,从而取出value;
根据源码分析,总结了几点:
1、threadLocal 变量为线程独享变量,所以可以用于需要线程隔离使用共享变量的场景
2、线程实例中维护的ThreadLocalMap由于线程池的原因可能会一直存在,所以map中的引用ThreadLocal设置为WeakReference,这样保证了ThreadLocal不再使用时可以GC