源码解读 | JDK源码 | ThreadLocal 原理解读

核心提炼

  • Thread 类有维护了一个属性变量 threadLocals (ThreadLocal.ThreadLocalMap threadLocals = null),也就是说每个线程有都一个自己的 ThreadLocalMap ,所以每个线程往这个 ThreadLocal 中读写隔离的,并且是互相不会影响的。

  • ThreadLocalMap 类是 ThreadLocal 的静态内部类

  • ThreadLocalMap 维护了一个 Entry 数组,Entry 的 key 是 ThreadLocal 对象,value 是存入的对象,所以一个 ThreadLocal 只能存储一个Object对象,如果需要存储多个Object对象那么就需要多个 ThreadLocal

  • Entry 的 key 引用 ThreadLocal 是弱引用

  • ThreadThreadLocalMapThreadLocal总览图如下
    在这里插入图片描述在这里插入图片描述

ThreadLocal 是用来干嘛的

ThreadLocal 主要是用在多线程的场景中

  • 保存线程上下文信息,在任意需要的地方可以获取 (比如下面案例中的保存用户信息)
  • 线程安全,避免某些情况需要考虑线程安全必须同步带来的性能损失

使用场景案例

在spring MVC开发中,我们常用 ThreadLocal 保存当前登陆用户信息,这样线程在任意地方都可以取到用户信息,比如我们会有以下类似下面的 UserContext 类,然后给配置一个拦截器,拦截器里面在请求执行前调用 UserContextsetUserInfo 方法将用户信息存入 ThreadLocal 对象 userInfoLocal 中,然后在请求的具体执行的任意地方调用 UserContextgetUserInfo 方法取出用户信息,最后在拦截器里面在请求结束返回前调用 UserContextclear 方法清除数据

public class UserContext {
    private static final ThreadLocal<UserInfo> userInfoLocal = new ThreadLocal<UserInfo>();

    public static UserInfo getUserInfo() {
        return userInfoLocal.get();
    }

    public static void setUserInfo(UserInfo userInfo) {
        userInfoLocal.set(userInfo);
    }

    public static void clear() {
        userInfoLocal.remove();
    }

}
ThreadLocal 使用代码示例
public class ThreadLocalTest {
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
    public static void main(String[] args) {
        new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    threadLocal.set(i);
                    System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get());
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                threadLocal.remove();
            }
        }, "thread-1").start();

        new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get());
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                threadLocal.remove();
            }
        }, "thread-2").start();
    }
}

运行结果
thread-1====0
thread-2====null
thread-2====null
thread-1====1
thread-2====null
thread-1====2
thread-2====null
thread-1====3
thread-2====null
thread-1====4

从运行结果可以看出,thread-1线程中对threadLocal对象的赋值对thread-2线程中threadLocal对象的值任何影响

源码细节

Thread 类
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 (类型是ThreadLocal.ThreadLocalMap),也就是说每个线程有一个自己的 ThreadLocalMap ,所以每个线程往这个ThreadLocal中读写隔离的,并且是互相不会影响的

一个ThreadLocal只能存储一个Object对象,如果需要存储多个Object对象那么就需要多个ThreadLocal

ThreadLocal 类
类签名
public class ThreadLocal<T> {

}
关键方法 | set
public void set(T value) {
        Thread t = Thread.currentThread();
  			// ** 取出当前线程维护的 ThreadLocalMap 对象 **
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

// 创建一个 ThreadLocalMap 赋值为当前 Thread 对象的属性,并添加第一个 Entry
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
关键方法 | get
public T get() {
        Thread t = Thread.currentThread();
  			// ** 取出当前线程维护的 ThreadLocalMap 对象 **
        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 对象
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
其他方法
public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
ThreadLocalMap 类

ThreadLocalMapThreadLocal的静态内部类

类签名
public class ThreadLocal<T> {
  	// ThreadLocalMap 是 ThreadLocal 的静态内部类
		static class ThreadLocalMap {
        
				// Entry 是 ThreadLocalMap 的静态内部类
      	static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

          	// Entry 的 key 是 ThreadLocal 对象,value 是关联的数据对象
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
      
        // ThreadLocalMap 维护了一个 Entry 数组,
      	private Entry[] table;
    }    
}
构造方法
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);
        }
关键方法 | set
private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);

  					// 如果 ThreadLocal 在 Entry 数组中已经存在,则替换其 value
            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;
                }
            }

  					// 不存在,则新建一个 Entry 插入到 Entry数组中
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
关键方法 | getEntry
private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
其他方法
/**
         * Remove the entry for key.
         */
        private void remove(ThreadLocal<?> key) {
            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)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }
ThreadLocalMap 里 Entry 为何声明为 WeakReference?
// Entry 是 ThreadLocalMap 的静态内部类
static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}
WeakReference是什么
  1. 强引用(StrongReference):存在就不会被gc回收,空间不足时报error
  2. 软引用(SoftReference):当空间不足时才会被gc回收。
  3. 弱引用(WeakReference):gc过程扫描到就会被回收。

WeakReference是Java语言规范中为了区别直接的对象引用(程序中通过构造函数声明出来的对象引用)而定义的另外一种引用关系。WeakReference标志性的特点是:reference实例不会影响到被应用对象的GC回收行为(即只要对象被除WeakReference对象之外所有的对象解除引用后,该对象便可以被GC回收),只不过在被对象回收之后,reference实例想获得被应用的对象时程序会返回null。

为什么ThreadLocalMap的key用弱引用,为什么不用强引用呢?

这里的弱引用可以首先由gc来判断ThreadLocal实例是否真的可以回收,由gc回收的结果,间接告诉我们,key为null了,这时候value也可以被清理了,并且最终通过高频操作get/set/remove封装好的方法进行清理。如果用强引用那么我们一直不知道这个entry是否可以被回收,除非强制每个coder在逻辑执行完的最后进行一次全局清理。

为什么value不用弱引用呢?

value不像key那样,还有一个外部的强引用,可能在业务执行过程中发生了gc,value被清理了,业务后边取值会出错的。

set/get/remove方法,这些方法会对key为null的entry进行释放

进阶 | FastThreadLocal

  • FastThreadLocal是 Netty 包中的类

  • FastThreadLocal的吞吐量是jdkThreadLocal的3倍左右

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器学习模型机器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值