ThreadLocal和InheritablThreadLocal核心源码详解

目录

一、ThreadLocal介绍

二、ThreadLocal核心方法解析

1) initialValue()

2) setIntialValue()

3) set()

4) get()

ThreadLocal是怎么做到多线程环境下副本隔离的?

三、ThreadLocalMap 源码解析

1) ThreadLocalMap是在什么时候初始化的呢?

2) Thread和value在ThreadLocalMap里是怎么存放的?

3) InheritablThreadLocal又是怎么存放的?

四、InheritableThreadLocal核心原理

1) inheritableThreadLocal究竟是何物?

2) 案例演示


一、ThreadLocal介绍

       ThreadLocal 能够保证在多线程情况下,每个线程独有一份对象副本,线程之间的副本互相独立、隔离。

       在每个线程类Thread实例里拥有二份ThreadLocal.ThreadLocalMap, 一份为当前线程的threadLocals,另一份为当前线程的inheritableThreadLocals, 他们都是静态内部类,因此这2个thredLocalMap在堆里都只有一份。inheritableThreadLocals能够将当前主线程设置的变量值传给子线程的inheritableThreadLocals。

ThreadLocal和InheritablThreadLocal核心源码详解_第1张图片

二、ThreadLocal核心方法解析

1) initialValue()

       用来初始化threadLocal的默认值, 默认为null。

protected T initialValue() {
    return null;
}		

2) setIntialValue()

       当set()方法被覆盖时,此方法替代set()方法的用法

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    // 获取当前线程的threadLocalMap, 如果为空那么createMap, 如果不为空,那么更新map的value值。
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
} 

3) set()

       给当前线程在ThreadLocalMap 设置一个value值,类型为自定义的T

public void set(T value) {
    // 1. 获取当前线程
    Thread t = Thread.currentThread();
    // 2. 获取当前线程的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    // 3. 查看map是否被初始化,如果被初始化了,那么覆盖掉之前的value, 否则初始化ThreadLocalMap
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

4) get()

     获取当前线程的value值。

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

ThreadLocal是怎么做到多线程环境下副本隔离的?

 是通过ThreadLocalMap实现的。

三、ThreadLocalMap 源码解析

         ThreadLocalMap是ThreadLocal类里的静态内部类, 搞懂ThreadLocalMap就能拨开迷雾见光明。

  static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;
}

跟着问题去探索ThreadLocalMap。

1) ThreadLocalMap是在什么时候初始化的呢?

       在调用set()方法时时候,然后会执行createMap。

ThreadLocal和InheritablThreadLocal核心源码详解_第2张图片

 createMap时,会将线程和value都交给ThreadLocalMap。

ThreadLocal和InheritablThreadLocal核心源码详解_第3张图片

2) Thread和value在ThreadLocalMap里是怎么存放的?

         每个线程存放的值是放在一个名为Entry静态内部类的value属性里,因此value为object类, 一个线程对应一个Entry, 在threadLocalMap类里有一个table, 该table就是用来存放所有线程(包含主线程和所有的子线程)的threadLocal的k和v, k为线程对象,v为object类型的value。

ThreadLocal和InheritablThreadLocal核心源码详解_第4张图片

3) InheritablThreadLocal又是怎么存放的?

       如果是inheritableThreadLocal, 那么创建的map也会添加到ThreadLocalMap静态内部类的Entry[] table里,这样就能保证所有的线程(包含主线程和所有子线程) 都能够独有一份变量副本。

ThreadLocal和InheritablThreadLocal核心源码详解_第5张图片

       经过一番调试后, 我发现所有的线程和副本最终都会放到ThreadLocalMap的Entry[] 里,这样就解释了每个线程之间的变量是独立的。

四、InheritableThreadLocal核心原理

1) inheritableThreadLocal究竟是何物?

       inheritableThreadLocal可将主线程设置的变量传到到子线程里。当不懂原理的时候,最好的方式就是debug源代码。

      子线程在初始化的时候,会进入到Thread类里的init()方法,然后会查看主线程的inheritableThreadLocal是否为空,如果不为空,那么就将主线程的inheritableThreadLocals复制一份给自己的inheritableThreadLocals。

ThreadLocal和InheritablThreadLocal核心源码详解_第6张图片

 init()方法代码:

    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
        ...
         // 将主线程的inheritableThreadLocals复制给子线程的inheritableThreadLocals
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

子线程的inheritableThreadLocals和主线程的一样,因此子线程也能拿到主线程设置的变量。

2) 案例演示

  private static InheritableThreadLocal inheritableThreadLocal = new InheritableThreadLocal<>();


    @Test
    void testThreadLocal() {
        inheritableThreadLocal.set(100);
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getId() +  "_" + inheritableThreadLocal.get());
            }).start();
        }
    }

 打印结果:

ThreadLocal和InheritablThreadLocal核心源码详解_第7张图片

你可能感兴趣的:(多线程和并发编程系列,threadlocal源码解析,threadLocal核心原理,inheritable,threadLocalMap,什么是ThreadLocal)