ThreadLocal源码分析

时间:2022-07-24
本文章向大家介绍ThreadLocal源码分析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

ThreadLocal源码分析

一. 简介

ThreadLocal是JDK提供的一个工具类,其作用是在多线程共享资源的情况下,使每个线程持有一份该资源的副本,每个线程的副本都是独立互不影响的。线程操作各自的副本,这样就避免了资源竞争引发的线程安全问题。

二. 源码分析

ThreadLocal,线程本地变量,该变量为每个线程私有。ThreadLocal类有一个内部类,名为ThreadLocalMap,可以理解为一个简化版的HashMap,它的Key为ThreadLocal实例,Value为ThreadLocal对象所引用的值,源码如下:

static class ThreadLocalMap {
	//该Map的Entry,Key为ThreadLocal实例,Value为ThreadLocal对象所引用的值。
    //这里使用了弱引用,当Entry为null时可以尽快被GC
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;

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

    //初始容量16
    private static final int INITIAL_CAPACITY = 16;

    
    private Entry[] table;
}

ThreadLocalMap内部保存了众多的ThreadLocal对象,那么既然说ThreadLocal是线程私有的,那么ThreadLocalMap是存放在哪里呢?

Thread类有一个成员变量——threadLocals,它就是保存了与当前Thread关联的一个ThreadLocalMap,源码如下:

//当前线程内部维护的ThreadLocalMap对象,用于保存所有ThreadLocal实例
ThreadLocal.ThreadLocalMap threadLocals = null;

可以看到,ThreadLocalMap对象保存在了Thread的内部,也即当前线程的私有内存中。

ThreadLocal的主要方法为get()、set()和initialValue()。首先看set():

public void set(T value) {
    //获取当前线程
    Thread t = Thread.currentThread();
    
    //获取当前线程关联的ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    
    //创建一个Entry,以当前ThreadLocal对象为Key,待存储对象为Value,保存在ThreadLocalMap中
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

可以看到,set()的逻辑很简单,从当前线程中获取ThreadLocalMap,然后将该ThreadLocal的值保存在里面。

再看get()方法:

public T get() {
    //获取当前线程对象
    Thread t = Thread.currentThread();
    
    //获取当前线程关联的ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    
    //从ThreadLocalMap获取以ThreadLocal为key的Entry的value
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    
    //如果当前ThreadLocalMap不存在,则调用setInitialValue()方法,获取初始值
    return setInitialValue();
}

ThreadLocal还有一个方法initialValue(),该方法提供给子类覆盖,以在创建ThreadLocal时指定初始值。

四. 应用场景

ThreadLocal最常见的使用场景为管理数据库连接Connection对象等。Spring中使用ThreadLocal来设计TransactionSynchronizationManager类,实现了事务管理与数据访问服务的解耦,同时也保证了多线程环境下connection的线程安全问题。

五. 注意事项

由于ThreadLocal对象会在每个线程中创建一个副本,所以在多线程环境下,如果ThreadLocal所保存的对象过大,可能导致内存的过多占用,需要引起注意。