当前位置:   article > 正文

通过ThreadLocal实现一个上下文管理组件_threadlocal传递上下文

threadlocal传递上下文

1 ThreadLocal原理

ThreadLocal主要作用就是实现线程间变量隔离,对于一个变量,每个线程维护一个自己的实例,防止多线程环境下的资源竞争,那ThreadLocal是如何实现这一特性的呢?

图1

从上图可知:

  1. 每个Thread对象中都包含一个ThreadLocal.ThreadLocalMap类型的threadlocals成员变量;
  2. 该map对应的每个元素Entry对象中:key是ThreadLocal对象的弱引用,value是该threadlocal变量在当前线程中的对应的变量实体;
  3. 当某一线程执行获取该ThreadLocal对象对应的变量时,首先从当前线程对象中获取对应的threadlocals哈希表,再以该ThreadLocal对象为key查询哈希表中对应的value;
  4. 由于每个线程独占一个threadlocals哈希表,因此线程间ThreadLocal对象对应的变量实体也是独占的,不存在竞争问题,也就避免了多线程问题。

有人可能会问:ThreadLocalMapThread成员变量(非public,只有包访问权限,Thread和Threadlocal都在java.lang 包下,Thread可以访问ThreadLocal.ThreadLocalMap),定义却在ThreadLocal中,为什么要这么设计?

源码的注释给出了解释:ThreadLocalMap就是维护线程本地变量设计的,就是让使用者知道ThreadLocalMap就只做保存线程局部变量这一件事。

set() 方法
public void set(T value) {
    Thread t = Thread.currentThread();	//获取当前线程
    ThreadLocalMap map = getMap(t);	//从当前线程对象中获取threadlocals,该map保存了所用的变量实例
    if (map != null) {
        map.set(this, value);
    } else {
        createMap(t, value);	//初始threadlocals,并设置当前变量
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
  • 1
  • 2
  • 3
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

  • 1
  • 2
  • 3
  • 4
get() 方法
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t); //从当前线程对象中获取threadlocals,该map保存了所用的变量实体
    if (map != null) {
        // 获取当前threadlocal对象对应的变量实体
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 如果map没有初始化,那么在这里初始化一下
    return setInitialValue();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
withInitial()方法

由于通过 ThreadLocalset() 设置的值,只会设置当前线程对应变量实体,无法实现统一初始化所有线程的ThreadLocal的值。ThreadLocal提供了一个 withInitial() 方法实现这一功能:

ThreadLocal<String> initValue = ThreadLocal.withInitial(() -> "initValue");

  • 1
  • 2
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
    // 返回SuppliedThreadLocal类型对象
    return new SuppliedThreadLocal<>(supplier);
}

  • 1
  • 2
  • 3
  • 4
  • 5
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {

    private final Supplier<? extends T> supplier;

    SuppliedThreadLocal(Supplier<? extends T> supplier) {
        this.supplier = Objects.requireNonNull(supplier);
    }

    @Override
    protected T initialValue() {
        // 获取初始化值
        return supplier.get();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
ThreadLocal中的内存泄漏问题

由图1可知,ThreadLocal.ThreadLocalMap 对应的Entry中,key为ThreadLocal对象的弱引用,方法执行对应栈帧中的ThreadLocal引用为强引用。当方法执行过程中,由于栈帧销毁或者主动释放等原因,释放了ThreadLocal对象的强引用,即表示该ThreadLocal对象可以被回收了。又因为Entry中key为ThreadLocal对象的弱引用,所以当jvm执行GC操作时是能够回收该ThreadLocal对象的。

而<

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/948957
推荐阅读
相关标签
  

闽ICP备14008679号