引言
无论实际项目实战还是面试,ThreadLocal
都是一个绕不开的话题,本文主要从源码角度和大家一起探讨下ThreadLocal
的神秘面纱。
ThreadLocal
是什么?它能干什么?ThreadLocal
源码分析- 总结
一、ThreadLocal
是什么?它能干什么?
ThreadLocal
是一个线程的本地变量, 也就意味着这个变量是线程独有的,是不能与其他线程共享的,它并不是解决多线程共享变量的问题。
所以ThreadLocal
与线程同步机制不同,线程同步机制是多个线程共享同一个变量,而ThreadLocal
是为每一个线程创建一个单独的变量副本,故而每个线程都可以独立地改变自己所拥有的变量副本,而不会影响其他线程所对应的副本。可以说ThreadLocal
为多线程环境下变量问题提供了另外一种解决思路。
ThreadLocal
的思想就是用空间换时间,使各线程都能访问属于自己这一份的变量副本,变量值不互相干扰,减少同一个线程内的多个函数或者组件之间一些公共变量传递的复杂度。
二、ThreadLocal
源码分析
1、ThreadLocalMap
解析
ThreadLocal
内部定义了一个ThreadLocalMap
的内部类,ThreadLocalMap
实际利用Entry
来实现key-value
的存储,如下所示:
static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value &#61; v;}}...
}
ThreadLocalMap
是实现线程隔离机制的关键&#xff0c;从以上代码可以看出Entry
的key
就是ThreadLocal
&#xff0c;而value
就是值。同时&#xff0c;Entry
也继承WeakReference
&#xff0c;所以说Entry
所对应key
&#xff08;ThreadLocal
实例&#xff09;的引用为一个弱引用。
我们主要来看下核心的getEntry()
、set(ThreadLocal> key, Object value)
方法
private Entry getEntry(ThreadLocal<?> key) {int i &#61; key.threadLocalHashCode & (table.length - 1);Entry e &#61; table[i];if (e !&#61; null && e.get() &#61;&#61; key)return e;elsereturn getEntryAfterMiss(key, i, e);}
private void set(ThreadLocal<?> key, Object value) {Entry[] tab &#61; table;int len &#61; tab.length;int i &#61; key.threadLocalHashCode & (len-1);for (Entry e &#61; tab[i];e !&#61; null;e &#61; tab[i &#61; nextIndex(i, len)]) {ThreadLocal<?> k &#61; e.get();if (k &#61;&#61; key) {e.value &#61; value;return;}if (k &#61;&#61; null) {replaceStaleEntry(key, value, i);return;}}tab[i] &#61; new Entry(key, value);int sz &#61; &#43;&#43;size;if (!cleanSomeSlots(i, sz) && sz >&#61; threshold)rehash();}
2、核心方法解析
(1) get()
返回此线程局部变量的当前线程副本中的值
public T get() {Thread t &#61; Thread.currentThread();ThreadLocalMap map &#61; getMap(t);if (map !&#61; null) {ThreadLocalMap.Entry e &#61; map.getEntry(this);if (e !&#61; null) {&#64;SuppressWarnings("unchecked")T result &#61; (T)e.value;return result;}}return setInitialValue();}
首先通过当前线程获取所对应的成员变量ThreadLocalMap
&#xff0c;然后通过ThreadLocalMap
获取当前ThreadLocal
的Entry
&#xff0c;最后通过所获取的Entry获取目标值result
。
(2) set(T value)
将此线程局部变量的当前线程副本中的值设置为指定值。
public void set(T value) {Thread t &#61; Thread.currentThread();ThreadLocalMap map &#61; getMap(t);if (map !&#61; null)map.set(this, value);elsecreateMap(t, value);}
获取当前线程所对应的ThreadLocalMap
&#xff0c;如果不为空&#xff0c;则调用ThreadLocalMap
的set()
方法&#xff0c;key就是当前ThreadLocal
&#xff0c;如果不存在&#xff0c;则调用createMap()
方法新建一个。
void createMap(Thread t, T firstValue) {t.threadLocals &#61; new ThreadLocalMap(this, firstValue);}
(3) initialValue()
返回此线程局部变量的当前线程的初始值。
protected T initialValue() {return null;}
该方法定义为protected
级别且返回为null
&#xff0c;需要其子类实现其功能&#xff0c;所以我们在使用ThreadLocal
的时候一般都应该覆盖该方法。该方法不能显示调用&#xff0c;只有在第一次调用get()
或者set()
方法时才会被执行&#xff0c;并且仅执行1次。
(4) remove()
移除此线程局部变量当前线程的值。
public void remove() {ThreadLocalMap m &#61; getMap(Thread.currentThread());if (m !&#61; null)m.remove(this);}
三、总结
1、多个线程去获取一个共享变量时&#xff0c;要求获取的是这个变量的初始值的副本。每个线程存储这个变量的副本&#xff0c;对这个变量副本的改变不会影响变量本身。适用于多个线程依赖不同变量值完成操作的场景。比如&#xff1a;
2、将ThreadLocal
设置成private static
的&#xff0c;这样ThreadLocal
会尽量和线程本身一起回收。