ThreadLocal的分析


ThreadLocal的分析

ThreadLocal的适用场景

  • ThreadLocal适用于保存每个线程都独享的对象
  • ThreadLocal适用于保存每个线程独享的对象可供其他方法获取的场景
    例如web应用程序中的用户信息可以保存中用线程中,从而在其他需要用到的地方直接可以获取到

ThreadLocal是不是用来解决共享资源多线程访问的问题?

  • 不是,ThreadLocal的底层机制决定了每个线程都独立拥有一份空间来存放数据。虽然在效果上线程安全的,但这并不是通过解决共享资源的方式实现的。

底层原理

每一个Thread内部都有一个ThreadLocalMap对象,在ThreadLocalMap中有保存的是ThreadLocal作为key,值作为value的k-v结构

ThreadLocal.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();
}

this就是ThreadLocal,因为ThreadLocalMap中的key就是ThreadLocal,因此通过getEntry(this)的方式取到对应的值

Thread.getMap()


/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

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

//InheritableThreadLocal
ThreadLocalMap getMap(Thread t) {
  return t.inheritableThreadLocals;
}

getMap()就是获取Thread的成员变量threadLocalsinheritableThreadLocals

  • ThreadLocalMap
    ThreadLocalMap
static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

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

Entry是一个内部类,类似于map结构。Entry这里不是采用HashMap的拉链法而是采用的是线性探测法
线性探测法原理如下:
线性探测法

ThreadLocal.remove

ThreadLocal.remove方法。在线程不使用ThreadLocal之后必须进行remove操作,因为在ThreadLocalMap中k-v是强引用关系,会造成内存泄漏。因此在不使用ThreaLocal之后一定要进行remove操作

InheritableThreadLocal

InheritableThreadLocal是子线程能访问到父线程的ThreadLoca

Thread.init()

if (inheritThreadLocals && parent.inheritableThreadLocals != null)
    this.inheritableThreadLocals =
        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

在线程初始化方法中,会判断父线程中的inheritableThreadLocals是否有值,如果有值会将值赋给子线程

总结

由于ThreadLocalMap中的Key是Thread,并且ThreadLocalMap与Key之间是弱引用的关系,Key与value之间是强引用的关系,导致Thread对象被回收后,还存在Key-Value之间的强引用关系,无法回收Value对象,造成内存泄漏;


  TOC