ThreadLocal 原理

2020-10-15  本文已影响0人  你可记得叫安可

用法

ThreadLocal<T> 是带了一个泛型 T 的,意思就是不同线程从同一个 ThreadLocal 实例中会取出属于自己的 T 类型的实例(这些实例在不同线程中是不同的)。
因此,ThreadLocal 适用于每个线程需要有一个自己独立的 T 类型实例变量,也就是说,变量需要在线程间隔离,但是在方法或类间共享的场景。

我们看 ThreadLocal 的注释中给我们示例了比较典型的用法:

public class ThreadId {
    // Atomic integer containing the next thread ID to be assigned
    private static final AtomicInteger nextId = new AtomicInteger(0);
    // Thread local variable containing each thread's ID
    private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return nextId.getAndIncrement();
        }
    };
    public static int get() {
        return threadId.get();
    }
}

一个 ThreadLocal 类型的静态变量 threadId,不同的线程通过调用 ThreadId.get 方法时所访问的 threadId 变量,都是独属于线程自己的 threadId 变量(既不同的实例)。

原理

其实 ThreadLocal 中,它的原理精髓在于内部类 ThreadLocalMap。在 ThreadLocalMap 中还有个内部类 Entry

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

Entry 是一个 WeakReference,它所持有的弱引用就是 ThreadLocal 实例。同时它还有一个类变量 value,也就是 ThreadLocal 所对应的值。

内部类中还有一个成员变量 table,它是一个 Entry 数组。我们上面说了,Entry 其实就是 ThreadLocal 的弱引用,而且每个线程 Thread 都有一个 ThreadLocalMap 类型的成员变量 threadLocals。因此,线程的 threadLocalstable,就是包含了属于该线程的所有 ThreadLocal 变量的数组集合。

ThreadLocal.get 方法

通过 ThreadLocal.get 方法,我们就能一窥 ThreadLocal 的原理。

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

当线程调用 get 方法时,通过 getMap(t) 方法能够取出该线程所持有的 ThreadLocalMap 类型的成员变量 Thread.threadLocals,也就是上面代码中的 map注意,这个 map 就已经是每个线程自己所独有的了。通过 map.getEntry(this) 可以从这个 ThreadLocalMaptable 中取出该 ThreadLocal 实例所对应的 Entry e,最后通过 e.value 来取出该 ThreadLocal 所对应的值。

上一篇下一篇

猜你喜欢

热点阅读