Java ThreadLocal使用

225 阅读3分钟

ThreadLocal叫做线程本地变量,它可以存储一些只有该线程使用的变量,而线程可以安全得访问ThreadLocal中存储的内容。

ThreadLocal的相关结构

ThreadLocalMap

ThreadLocal中有一个静态内部类ThreadLocalMap,ThreadLocalMap用于存储具体的变量内容。ThreadLocalMap是一个Map结构,key为ThreadLocal,值是Entry变量,且该变量是一个弱引用。

ThreadLocalMap部分定义如下:

static class ThreadLocalMap {
    /**
     * Entry继承弱引用WeakReference
     */
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /**
         * value值
         */
        Object value;

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

    // 默认数组长度
    private static final int INITIAL_CAPACITY = 16;

    // 数组
    private Entry[] table;

    // 从ThreadLocalMap中获取本地变量值,key为ThreadLocal
    private ThreadLocal.ThreadLocalMap.Entry getEntry(ThreadLocal<?> key) {
        //...
    }

    // 向ThreadLocalMap中设置本地变量值,key为ThreadLocal
    private void set(ThreadLocal<?> key, Object value) {
        //...
    }
}

ThreadLocal

ThreadLocal提供访问,设置,删除本地变量的方法,实质上是对ThreadLoalMap的操作。

ThreadLocal的方法如下:

  • set:为当前线程设置变量,当前ThreadLocal作为索引
  • get:获取当前线程变量,当前ThreadLocal作为索引
  • initialValue(钩子方法需要子类实现):懒加载形式初始化线程本地变量,执行get时,发现线程本地变量为null,就会执行initialValue的内容
  • remove:清空当前线程的ThreadLocal索引与映射的元素

Thread

Thread类中声明了成员变量threadLocals(ThreadLocal.ThreadLocalMap threadLocals = null;),threadLcoals是ThreadLocalMap的一个对象,可以存储线程本地变量。由于每个Thread都有自己的线程本地变量,所以线程本地变量拥有线程隔离性,也就是线程可以安全得访问。

一个Thread可以有多个ThreadLocal用于存储不同类型的本地变量,这些变量都存储在ThreadLocalMap中,其中ThreadLocal为key,Entry包装的变量为value,并且可以通过ThreadLocal提供的方法访问,设置,删除这些本地变量。

ThreadLocal提供的方法

一个ThreadLocal使用的实例

public class UserContext {
    ThreadLocal<String> threadLocal = new ThreadLocal(){
        @Override
        protected String initialValue(){
            return "User";
        }
    };

    public String getUserInfo(){
        return threadLocal.get();
    }

    public void setUserInfo(String userInfo){
        threadLocal.set(userInfo);
    }

    public void removeUserInfo(){
        threadLocal.remove();
    }
}

getMap方法

getMap()方法用于获取该线程threadLocals变量,即实际上存储本地变量的ThreadlLocalMap。

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

get方法

get()方法用于获取当前线程存储的本地变量。

public T get() {
    // 当前线程
    Thread t = Thread.currentThread();
    // 线程的threadLocals变量
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 当前ThreadLocal为key,在map中获取Entry变量
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 如果没有获取到对应的值,执行initialValue()方法
    return setInitialValue();
}

set方法

set()方法用于设置当前线程存储的本地变量

public void set(T value) {
    // 当前线程
    Thread t = Thread.currentThread();
    // 线程的threadLocals变量
    ThreadLocalMap map = getMap(t);
    if (map != null)
        // map不为空,直接添加值,当前ThreadLocal为key
        map.set(this, value);
    else
       // map为空,先创建ThreadLocalMap,再设置值
        createMap(t, value);
}

remove方法

remove()方法用于清除本地变量。

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

通过在业务中,一个线程的生命周期很长,一个线程处理完第一个任务之后会再去处理第二个任务,若不及时清理本地存储的变量,则上一个任务用到的变量对被带到下一个任务中使用,很可能导致意想不到的结果。

remove使用场景:

  • 生命周期较长的线程场景
  • 无限循环线程的场景
  • 线程池场景