inheritableThreadLocal源码分析

InheritableThreadLocal是ThreadLocal的子类,当父线程创建一个InheritableThreadLocal对象之后,InheritableThreadLocal的内容能够在这个父线程的所有子线程中共享。这个实现相当有意义。比如可以利用这个类实现多线程的共享事务。这个类实际上非常简单,在Thread源码中对其做了支持。

一、使用

1、测试

有如下测试类,在main函数的主线程中给InheritableThreadLocal设置了一个字符串。之后在这个主线程的各个子线程中进行读取。但是如果在子线程中设值,主线程是不会接收到的。如下:

public class InheritableThreadLocalTest {

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

   public static void main(String[] args) throws Exception{
      itl.set("主线程赋值");
      System.out.println("value:"+itl.get());
      new Thread(() -> {
         try {
            TimeUnit.SECONDS.sleep(1);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
         System.out.println("子线程1获取:"+itl.get());
         itl.set("子线程1设值");
         System.out.println("子线程1设值后再次获取:"+itl.get());
      }).start();
      new Thread(() -> {
         System.out.println("子线程2获取:"+itl.get());
      }).start();

      TimeUnit.SECONDS.sleep(2);
      System.out.println("主线程在子线程1设置之后获取值:"+itl.get());
   }

}

运行结果如下:

value:主线程赋值
子线程2获取:主线程赋值
子线程1获取:主线程赋值
子线程1设值后再次获取:子线程1设值
主线程在子线程1设置之后获取值:主线程赋值

二、inberitableThreadLocaly源码

/**
 * This class extends {@code ThreadLocal} 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 verriding the {@code childValue} 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.
 *
 * <p>Note: During the creation of a new {@link
 * Thread#Thread(ThreadGroup,Runnable,String,long,boolean) thread}, it is
 * possible to <i>opt out</i> of receiving initial values for inheritable
 * thread-local variables.
 *
 * @author  Josh Bloch and Doug Lea
 * @see     ThreadLocal
 * @since   1.2
 */

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    /**
     * Creates an inheritable thread local variable.
     */
    public InheritableThreadLocal() {}

    /**
     * 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代码进行分析。

三、Thread源码

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

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

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

而这也解决了在之前分析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方法那么让人耳目一新,但是也是我们自己在做架构设计的时候值得借鉴的。

四、总结

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或者内存泄漏。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值