zoukankan      html  css  js  c++  java
  • 深入理解ThreadLocal(二)

    3 InheritableThreadLocal的使用

    通过上面的分析知道通过ThreadLocal保存的值是线程隔离的。其实在Thread对象中,还有一个ThreadLocal.ThreadLocalMap类型的成员变量inheritableThreadLocals。inheritableThreadLocals其实是一个InheritableThreadLocals类型,InheritableThreadLocals是ThreadLocal的子类。保存在inheritableThreadLocals中的值可以传递给子线程。下面看一个例子:

    [java] view plain copy 

    1. public class InheritableThreadLocalTest {  

    2. static InheritableThreadLocal<Long> mInheritableThreadLocal = new InheritableThreadLocal<Long>();  

    3. static ThreadLocal<Long> mThreadLocal = new ThreadLocal<Long>();  

    4. static SimpleInheritableThreadLocal<Long> mSimpleInheritableThreadLocal=new SimpleInheritableThreadLocal<Long>();  

    5. public static void printValue() {  

    6.    

    7. System.out.println("Thread " + Thread.currentThread().getId() + ":  valueFromParent=" + mInheritableThreadLocal.get() + "  valueFromLocal="  

    8. + mThreadLocal.get() +" simpleValueFromParent="+mSimpleInheritableThreadLocal.get());  

    9. }  

    10.    

    11. static class PrintValueRunnable implements Runnable {  

    12.    

    13. @Override  

    14. public void run() {  

    15. // TODO Auto-generated method stub  

    16. InheritableThreadLocalTest.printValue();  

    17. if (Thread.currentThread().getId() % 2 == 0) {  

    18. mInheritableThreadLocal.set(mInheritableThreadLocal.get()+1);  

    19. }  

    20. InheritableThreadLocalTest.printValue();  

    21. }  

    22. }  

    23.    

    24. public static void main(String[] args) {  

    25.    

    26. long tid = Thread.currentThread().getId();  

    27. mInheritableThreadLocal.set(tid);  

    28. mThreadLocal.set(tid);  

    29. mSimpleInheritableThreadLocal.set(tid);  

    30. System.out.println("mainThread: " + "  valueFromLocal=" + mThreadLocal.get());  

    31. new Thread(new PrintValueRunnable()).start();  

    32. new Thread(new PrintValueRunnable()).start();  

    33. new Thread(new PrintValueRunnable()).start();  

    34. }  

    35. }  

    打印结果如下:

     

    在这个例子,先在主线程中new一个InheritableThreadLocal对象,然后在子线程中访问。通过打印结果可以看到,主线程设置到InheritableThreadLocal中的值会复制给子线程,然后父子线程可以各自独立读写保存在InheritableThreadLocal中的值。可以还有人注意到了,在上面的例子中还有一个SimpleInheritableThreadLocal对象,这个是我自定义,它继承了InheritableThreadLocal,并重写了InheritableThreadLocal.childValue。我们可以通过重写childValue来控制从父线程中继承过来的value的初始值。在SimpleInheritableThreadLocal中会对从父线程继承的Long对象做加1处理。默认情况下,是不做任何处理的。SimpleInheritableThreadLocal的代码如下:

    [java] view plain copy 

    1. public class SimpleInheritableThreadLocal<T> extends InheritableThreadLocal<T> {  

    2.    

    3. @Override  

    4. protected T childValue(T parentValue) {  

    5. // TODO Auto-generated method stub  

    6. if (parentValue instanceof Long) {  

    7. Long res = (Long) parentValue + 1;  

    8. return (T) res;  

    9. }  

    10.    

    11. return super.childValue(parentValue);  

    12. }  

    13. }  

    可能有人会有疑问:ThreadLocal是线程隔离的,而InheritableThreadLocal又是ThreadLocal的子类,所以InheritableThreadLocal也应该是线程隔离的。那这里是怎么做到从父线程继承值呢?子线想一想:实现线程隔离的核心思想是将要隔离的变量保存到Thread对象里面,然后通过ThreadLocal来读写这个变量;其实不通过ThreadLocal,在Thread内部是可以直接读写threadLocals的。那既然inheritableThreadLocals是Thread的成员变量,那么在初始化Thread的时候就可以直接读取父线程的inheritableThreadLocals并把它保存的values设置为子线程的inheritableThreadLocals初始值。

    这个想法对不对呢?我们来看一下Thread初始化部分的源码:

    [java] view plain copy 

    1. public Thread(Runnable target) {  

    2.         init(null, target, "Thread-" + nextThreadNum(), 0);  

    3. }  

    4. private void init(ThreadGroup g, Runnable target, String name,  

    5.                       long stackSize) {  

    6.         init(g, target, name, stackSize, null);  

    7.     }  

    8.  private void init(ThreadGroup g, Runnable target, String name,  

    9.                       long stackSize, AccessControlContext acc) {  

    10.         if (name == null) {  

    11.             throw new NullPointerException("name cannot be null");  

    12.         }  

    13.    

    14.         this.name = name.toCharArray();  

    15.    

    16.         Thread parent = currentThread();  

    17.         SecurityManager security = System.getSecurityManager();  

    18.         if (g == null) {  

    19.             /* Determine if it's an applet or not */  

    20.    

    21.             /* If there is a security manager, ask the security manager 

    22.                what to do. */  

    23.             if (security != null) {  

    24.                 g = security.getThreadGroup();  

    25.             }  

    26.    

    27.             /* If the security doesn't have a strong opinion of the matter 

    28.                use the parent thread group. */  

    29.             if (g == null) {  

    30.                 g = parent.getThreadGroup();  

    31.             }  

    32.         }  

    33.    

    34.         /* checkAccess regardless of whether or not threadgroup is 

    35.            explicitly passed in. */  

    36.         g.checkAccess();  

    37.    

    38.         /* 

    39.          * Do we have the required permissions? 

    40.          */  

    41.         if (security != null) {  

    42.             if (isCCLOverridden(getClass())) {  

    43.                 security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);  

    44.             }  

    45.         }  

    46.    

    47.         g.addUnstarted();  

    48.    

    49.         this.group = g;  

    50.         this.daemon = parent.isDaemon();  

    51.         this.priority = parent.getPriority();  

    52.         if (security == null || isCCLOverridden(parent.getClass()))  

    53.             this.contextClassLoader = parent.getContextClassLoader();  

    54.         else  

    55.             this.contextClassLoader = parent.contextClassLoader;  

    56.         this.inheritedAccessControlContext =  

    57.                 acc != null ? acc : AccessController.getContext();  

    58.         this.target = target;  

    59.         setPriority(priority);  

    60.         if (parent.inheritableThreadLocals != null)  

    61.             this.inheritableThreadLocals =  

    62.                 ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);  

    63.         /* Stash the specified stack size in case the VM cares */  

    64.         this.stackSize = stackSize;  

    65.    

    66.         /* Set thread ID */  

    67.         tid = nextThreadID();  

    68. }  

    先看Thread的构造函数,这里调用了init函数,最终会调用这init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc)函数。直接看第60行,如果父线程的parent.inheritableThreadLocals不为null,就会以父线程的inheritableThreadLocals为参数来构造子线程的inheritableThreadLocals。我们再看一下createInheritedMap函数:

    [java] view plain copy 

    1. static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {  

    2.         return new ThreadLocalMap(parentMap);  

    3. }  

    4.  private ThreadLocalMap(ThreadLocalMap parentMap) {  

    5.             Entry[] parentTable = parentMap.table;  

    6.             int len = parentTable.length;  

    7.             setThreshold(len);  

    8.             table = new Entry[len];  

    9.    

    10.             for (int j = 0; j < len; j++) {  

    11.                 Entry e = parentTable[j];  

    12.                 if (e != null) {  

    13.                     ThreadLocal key = e.get();  

    14.                     if (key != null) {  

    15.                         Object value = key.childValue(e.value);  

    16.                         Entry c = new Entry(key, value);  

    17.                         int h = key.threadLocalHashCode & (len - 1);  

    18.                         while (table[h] != null)  

    19.                             h = nextIndex(h, len);  

    20.                         table[h] = c;  

    21.                         size++;  

    22.                     }  

    23.                 }  

    24.             }  

    25.         }  

    到这里就可以看到:在ThreadLocalMap的构造函数中会遍历保存父线程的inheritableThreadLocals中键值对,并且在读取value的会调用key.childValue计算子线程中的value。所以在SimpleInheritableThreadLocal重写了childValue来根据父线程的value计算子线程的value,默认情况是不做任何处理,直接返回的,如:

    [java] view plain copy 

    1. T childValue(T parentValue) {  

    2.         throw new UnsupportedOperationException();  

    3.     }  

  • 相关阅读:
    算法训练(大富翁)
    算法训练题(奖励最小)
    算法训练题
    乔布斯
    算法题(符合题意的6位数)
    算法题(八皇后)
    算法题(分小组)
    汉诺塔
    递归算法题(兔子)
    字符串-mask-每个元音包含偶数次的最长子字符串
  • 原文地址:https://www.cnblogs.com/downey/p/5302075.html
Copyright © 2011-2022 走看看