其他分享
首页 > 其他分享> > 透彻理解ThreadLocal

透彻理解ThreadLocal

作者:互联网

作用和使用场景

内存模型图

看如下代码示例及其对应的内存模型图

public class ThreadLocalTest {
    @Test
    public void test() {
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        Thread thread = new Thread(() -> threadLocal.set("value"));
        thread.start();
    }    
}

ThreadLocalMap

从内存图知,ThreadLocalMap是一个容器类,key是threadLocal对象,value是我们要保存的对象,threadLocal赋值的key和value都是保存在各自线程的threadLocalMap对象中的。threadLocalMap对象底层也是entry数组(注意这个entry和hashMap中的entry不是同一个类),而与HashMap不同的是,他们解决hash冲突的方式不一样,threadLocalMap使用开放寻址法(线性探测),而hashMap使用的是链地址法

ThreadLocal源码详解

get方法

/**
  * Returns the value in the current thread's copy of this
  * thread-local variable.  If the variable has no value for the
  * current thread, it is first initialized to the value returned
  * by an invocation of the {@link #initialValue} method.
  *
  * @return the current thread's value of this thread-local
  */
public T get() {
    // 获取当前线程
    Thread t = Thread.currentThread();
    // 得到当前线程的threadLocals属性对象(即Entry[]对象)
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        // 以threadLocal对象做为key,通过“开放定址法”去寻找对应位置的entry
        // 特别注意:在寻找过程中,会对遇到的无效entry进行清除以避免内存泄漏
        // 解释:"清除无效entry"即打断如内存图所示的”引用7“和”引用5“的引用
        ThreadLocalMap.Entry e = map.getEntry(this);
        // 若该entry对象不为null则返回该entry.value
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    // 若当前线程的threadLocals属性对象为null,或者未找对应的entry,返回初始化的值
    return setInitialValue();
}

set方法

/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    // 获取当前线程
    Thread t = Thread.currentThread();
    // 得到当前线程的threadLocals属性对象(即Entry[]对象)
    ThreadLocalMap map = getMap(t);
    if (map != null)
        // 以threadLocal对象做为key,通过“开放定址法”去寻找对应位置的entry,若找到正确的entry,则直接对该entry.value赋值,若未找到则new一个新的entry放在正确的位置。
        // 特别注意:在寻找entry的过程中,会对遇到的无效entry进行清除避免内存泄漏。必要时会进行扩容(扩容前会清除所有的无效entry再判断是否需要扩容)
        // 解释:"清除无效entry"即打断如内存图所示的”引用7“和”引用5“的引用
        map.set(this, value);
    else
        // 新建一个ThreadLocalMap对象,赋值到该线程t的threadLocals属性上,然后将key(threadLocal对象)和value对象放到map中正确的位置
        createMap(t, value);
}

remove方法

public void remove() {
    // 得到当前线程的threadLocals属性对象(即Entry[]对象)
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        // 相当于将内存图中的”引用5、引用6、引用7“全部指向null
        m.remove(this);
}

内存泄漏问题

内存泄漏现象是存在无用的且一直无法被垃圾回收器GC掉的对象。而使用threadLocal涉及到的内存泄漏指的是各个线程中threadLcoalMap属性对象的entry数组引用到的一些无用的对象(即无效的entry)。这里分如下2个方面来讲

标签:对象,threadLocal,value,ThreadLocal,理解,线程,内存,entry,透彻
来源: https://blog.csdn.net/zhukai_boke/article/details/118461202