一, 引入
- public class Thread implements Runnable {
- /* 前面略 */
- /* ThreadLocal values pertaining to this thread. This map is maintained
- * by the ThreadLocal class. */
- ThreadLocal.ThreadLocalMap threadLocals = null;
- /* 后面略 */
- }
首先我们看到的是 Thread 中有一个属性 threadLocals, 它的类型是 ThreadLocalMap, 封装类型是 default(表示它只能在包内可见),jdk 是这么介绍它的: 与此线程有关的 ThreadLocal 值, 该映射由 ThreadLocal 类维护. 啥意思呢? 那就来看看 ThreadLocalMap 是啥玩意!
- public class ThreadLocal<T> {
- /* 前面略 */
- static class ThreadLocalMap {
- /**
- * The entries in this hash map extend WeakReference, using
- * its main ref field as the key (which is always a
- * ThreadLocal object). Note that null keys (i.e. entry.get()
- * == null) mean that the key is no longer referenced, so the
- * entry can be expunged from table. Such entries are referred to
- * as "stale entries" in the code that follows.
- */
- static class Entry extends WeakReference<ThreadLocal<?>> {
- /** The value associated with this ThreadLocal. */
- Object value;
- Entry(ThreadLocal<?> k, Object v) {
- super(k);
- value = v;
- }
- }
- /* 后面略 */
- }
- }
从类定义上可以看出 ThreadLocal 是支持泛型的, 而 ThreadLocalMap 是 ThreadLocal 的一个内部类, 封装类型也是 default(表示只能在包内可见),jdk 是这么介绍它的: ThreadLocalMap 是自定义的哈希映射, 仅适用于维护线程局部值. 并且为了存储容量可控, 不至于内存泄漏, 哈希表条目使用弱引用作为键 (弱引用的对象的生命周期直到下一次垃圾回收之前被回收),ThreadLocalMap 使用静态内部类 Entry(可以类比 Map 中的 entry) 来存储实际的 key 和 value.
从上面这些介绍, 我们可以大致想到, ThreadLocal 是一个与线程相关的类, 用来存储维护线程局部值.
二, set(T value) 方法解读
- public void set(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- }
- ThreadLocalMap getMap(Thread t) {
- return t.threadLocals;
- }
- void createMap(Thread t, T firstValue) {
- t.threadLocals = new ThreadLocalMap(this, firstValue);
- }
可以看到 set(T value) 方法做的事情很简单, 就是维护 Thread 的 threadLocals 属性, 如果该属性不存在的话, 就以当前 ThreadLocal 实例为 key 创建一个; 该属性存在的话, 则直接赋值.
三, 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();
- }
- 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;
- }
- protected T initialValue() {
- return null;
- }
get() 的方法同样很简单, 就是从 Thread 的 threadLocals 属性获取值, 如果获取不到, 则把 initialValue() 的值赋值给线程的 threadLocals 属性并返回. initialValue() 方法是一个 protected 类型的方法, 默认返回 null, 我们可以在创建 ThreadLocal 的时候重写它, 表示所有线程的默认值.
- // java8 的方式
- ThreadLocal<Boolean> threadLocal1 = ThreadLocal.withInitial(() -> false);
- //
- ThreadLocal<Boolean> threadLocal2 = new ThreadLocal<Boolean>() {
- @Override
- protected Boolean initialValue() {
- return false;
- }
- };
四, 总结
ThreadLocal 用于存储维护线程的局部值.
和 ThreadLocal 类似的还有一个叫 InheritableThreadLocal, InheritableThreadLocal 继承自 ThreadLocal, 用于父子线程间共享共同的值, 父线程中设置的值, 子线程中可以访问到.
上面看 ThreadLocalMap 的时候, 我们知道 key 是弱引用, gc 的时候 key 会被回收, 但是 value 和 ThreadLocalMap 的引用不会被回收, 如果这种情况的 Thread 很多, 而且一直没有执行完, 就可能会出现内存泄漏, 因此使用完 ThreadLocal 的时候尽量调用 ThreadLocal 的 remove() 方法.
当使用线程池的时候, 在调用 ThreadLocal 的 set 方法后, 却没有调用 remove 的方法, 如果同一个线程再去调用 get 方法可能拿到的值并不是当时 set 进去的(因为线程池的线程是复用的), 可能导致程序数据异常之类的, 因此使用完 ThreadLocal 的时候尽量调用 ThreadLocal 的 remove() 方法.
来源: https://www.cnblogs.com/jmcui/p/11994720.html