java的reference(五): WeakReference的应用之二--InheritableThreadLocal源码分析

时间:2022-07-22
本文章向大家介绍java的reference(五): WeakReference的应用之二--InheritableThreadLocal源码分析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在上一篇中具体讨论了ThreadLocal的源码及ThreadLocalMap的核心代码。还有一个相对没那么重要的内容没有讨论,那就是 InheritableThreadLocal。InheritableThreadLocal是ThreadLocal的子类,当父线程创建一个InheritableThreadLocal对象之后,InheritableThreadLocal的内容能够在这个父线程的所有子线程中共享。这个实现相当有意义。比如可以利用这个类实现多线程的共享事务。这个类实际上非常简单,在Thread源码中对其做了支持。

1.demo

有如下测试类,在main函数的主线程中给InheritableThreadLocal设置了一个字符串。之后在这个主线程的各个子线程中进行读取。

public class InheritableThreadLocalTest {

	private static final InheritableThreadLocal<String> itl = new InheritableThreadLocal<>();

	public static void main(String[] args) {
		itl.set("主线程赋值");
		System.out.println("value:"+itl.get());
		new Thread(() -> {
			System.out.println("子线程1获取:"+itl.get());
		}).start();
		new Thread(() -> {
			System.out.println("子线程2获取:"+itl.get());
		}).start();
	}

}

上面代码执行结果如下:

value:主线程赋值
子线程1获取:主线程赋值
子线程2获取:主线程赋值

Process finished with exit code 0

2.InheritableThreadLocal源码

InheritableThreadLocal源码非常简单,继承了ThreadLocal,因此其本质就是一个ThreadLocal。只是将三个方法进行了重写,childValue、getMap和createMap。

/**
 * This class extends <tt>ThreadLocal</tt> to provide inheritance of values
 * from parent thread to child thread: when a child thread is created, the
 * child receives initial values for all inheritable thread-local variables
 * for which the parent has values.  Normally the child's values will be
 * identical to the parent's; however, the child's value can be made an
 * arbitrary function of the parent's by overriding the <tt>childValue</tt>
 * method in this class.
 *
 * <p>Inheritable thread-local variables are used in preference to
 * ordinary thread-local variables when the per-thread-attribute being
 * maintained in the variable (e.g., User ID, Transaction ID) must be
 * automatically transmitted to any child threads that are created.
 *
 * @author  Josh Bloch and Doug Lea
 * @see     ThreadLocal
 * @since   1.2
 */

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     * Computes the child's initial value for this inheritable thread-local
     * variable as a function of the parent's value at the time the child
     * thread is created.  This method is called from within the parent
     * thread before the child is started.
     * <p>
     * This method merely returns its input argument, and should be overridden
     * if a different behavior is desired.
     *
     * @param parentValue the parent thread's value
     * @return the child thread's initial value
     */
    protected T childValue(T parentValue) {
        return parentValue;
    }

    /**
     * Get the map associated with a ThreadLocal.
     *
     * @param t the current thread
     */
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    /**
     * Create the map associated with a ThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the table.
     */
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }

在分析ThreadLocal的代码中,set和get的方法中,都有getMap和createMap。这两个方法重写了之后,就将之前的从Thread中的threadLoccals获取threadLocalMap变成了从inheritableThreadLocals获取ThreadLocalMap。 在看完这些代码之后,还是没有明白,是如何将ThreadLocalMap的内容放置到Thread的inheritableThreadLocals变量的。 因此需要进一步对Thread代码进行分析。

3.Thread源码

在Thread源码的init方法中,可以看到:

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null);
    }
    
     private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        
        ... ...
        //与本文最关键的部分
         if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}

可以看到,实际上在创建Thread的时候,会从父线程中去判断inheritableThreadLocals 是否为空,如果不为空,则调用ThreadLocal的createInheriteMap方法。

  static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }

而这也解决了在之前分析ThreadLocal源码中的一个疑问。之前在ThreadLocal的初始化方法中:

 private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

上面的代码要用到 key.childValue(e.value);。这个方法。而这个方法在ThreadLocal中:

  T childValue(T parentValue) {
        throw new UnsupportedOperationException();
    }

这个方法没有返回值,只会抛出一个异常。当时的备注页明确说明了:

Construct a new map including all Inheritable ThreadLocals
from given parent map. Called only by createInheritedMap.

这个批量创建ThreadLocalMap的方法只能用在createInheritedMap的时候,否则就会抛出异常。而使用createInheritedMap的时候,子类重写了childValue方法:

  protected T childValue(T parentValue) {
        return parentValue;
    }

这样就不会抛出异常。这也是面向对象多态特性的一种 具体的应用。虽然其设计不如ThreadLocalMap及WeakReference方法那么让人耳目一新,但是也是我们自己在做架构设计的时候值得借鉴的。

4.总结

1.InheritableThreadLocal在线程创建的时候,从父线程中拷贝了inheritableThreadLocals,这是一个相对的深度拷贝,重建了整个ThreadLocalMap。如果Entry的value不是引用类型,那么这些Entry的值在每个Thread中互不影响。由于只copy到Entry这一级,如果Entry的value本身就是引用类型,那么将会共享。 2.InheritableThreadLocal利用了面向对象的多态特性,重写了childValue、getMap和createMap方法。在Thread中对inheritableThreadLocals进行了处理。这说明,如果在Thread的基础上实现共享内存或者事务等,只能使用ThreadLocal或者InheritableThreadLocal来实现。 3.InheritableThreadLocal与ThreadLocal会有相同的内存泄漏的风险。因此需要注意对remove方法的使用。避免导致OOM或者内存泄漏。