JUC 之 ThreadLocal 分析

2023-10-22  本文已影响0人  Tinyspot

1. ThreadLocal 类

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

1.2 getMap(Thread)

获取当前线程的 threadLocals 变量 (每个线程都有一个自己的ThreadLocalMap)

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

取自 Thread 类的 ThreadLocal.ThreadLocalMap threadLocals = null;

public class Thread implements Runnable {
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}

Thread 有一个 ThreadLocalMap 类型的成员变量 threadLocals, 该变量用来存储变量副本,存储以 ThreadLocal 为键,本地线程变量为值的键值对

1.3 set(T value)

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

1.4 createMap(Thread, T)

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);
}

1.5 initialValue()

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

protected T initialValue() {
    return null;
}

1.6 remove()

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

2. ThreadLocalMap 类

public class ThreadLocal<T> {
    
    static class ThreadLocalMap {
        
        static class Entry extends WeakReference<ThreadLocal<?>> { }
    }
}
  1. ThreadLocal 里有静态内部类ThreadLocalMap
  2. ThreadLocalMap 里有静态内部类 Entry 用来存放数据(键值对),且继承弱引用 WeakReference

2.1 Entry 类

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

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

ThreadLocal<?> 弱引用 作为 key

2.2 set(ThreadLocal<?> key, Object value)

private void set(ThreadLocal<?> key, Object value) {
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);

    //...
    tab[i] = new Entry(key, value);
    // ...
}

3. ThreadLocal 内存泄露问题

内存泄露,即某个对象不再有用,但是占用的内存却不能被回收

ThreadLocalMap

虽然 ThreadLocalMap 中的 key 是弱引用,当不存在外部强引用的时候,就会自动被回收,但是 Entry 中的 value 依然是强引用。这个 value 的引用链条如下:

image.png

可以看到,只有当 Thread 被回收时,这个 value 才有被回收的机会,否则,只要线程不退出 (比如线程池需要保持很久),value 总是会存在一个强引用。

3.1 如何避免内存泄露

调用 remove 方法,就会删除对应的 Entry 对象,可以避免内存泄露

4. ThreadLocal 应用场景

4.1 应用场景

场景一:全局存储用户信息
场景二:在Spring事务管理中的应用ThreadLocal

4.2 ThreadLocal 慎用的场景

场景一:线程池里线程调用 ThreadLocal,
线程池的线程都是复用的,在线程池中线程非常难结束甚至于永远不会结束,线程持续的时间将不可预测

场景二:异步程序中使用ThreadLocal
因为线程将请求发送后,不会等待远程返回结果,而是继续向下运行,真正的返回结果得到后,处理的线程可能是其他的线程

上一篇下一篇

猜你喜欢

热点阅读