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. System.out.println("Thread " + Thread.currentThread().getId() + ":\t valueFromParent=" + mInheritableThreadLocal.get() + "\t valueFromLocal="

  7. + mThreadLocal.get() +"\tsimpleValueFromParent="+mSimpleInheritableThreadLocal.get());

  8. }

  9. static class PrintValueRunnable implements Runnable {

  10. @Override

  11. public void run() {

  12. // TODO Auto-generated method stub

  13. InheritableThreadLocalTest.printValue();

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

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

  16. }

  17. InheritableThreadLocalTest.printValue();

  18. }

  19. }

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

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

  22. mInheritableThreadLocal.set(tid);

  23. mThreadLocal.set(tid);

  24. mSimpleInheritableThreadLocal.set(tid);

  25. System.out.println("mainThread: " + "\t valueFromLocal=" + mThreadLocal.get());

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

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

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

  29. }

  30. }

打印结果如下:

在这个例子,先在主线程中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. @Override

  3. protected T childValue(T parentValue) {

  4. // TODO Auto-generated method stub

  5. if (parentValue instanceof Long) {

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

  7. return (T) res;

  8. }

  9. return super.childValue(parentValue);

  10. }

  11. }

可能有人会有疑问: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. this.name = name.toCharArray();

  14. Thread parent = currentThread();

  15. SecurityManager security = System.getSecurityManager();

  16. if (g == null) {

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

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

  19. what to do. */

  20. if (security != null) {

  21. g = security.getThreadGroup();

  22. }

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

  24. use the parent thread group. */

  25. if (g == null) {

  26. g = parent.getThreadGroup();

  27. }

  28. }

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

  30. explicitly passed in. */

  31. g.checkAccess();

  32. /*

  33. * Do we have the required permissions?

  34. */

  35. if (security != null) {

  36. if (isCCLOverridden(getClass())) {

  37. security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);

  38. }

  39. }

  40. g.addUnstarted();

  41. this.group = g;

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

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

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

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

  46. else

  47. this.contextClassLoader = parent.contextClassLoader;

  48. this.inheritedAccessControlContext =

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

  50. this.target = target;

  51. setPriority(priority);

  52. if (parent.inheritableThreadLocals != null)

  53. this.inheritableThreadLocals =

  54. ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

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

  56. this.stackSize = stackSize;

  57. /* Set thread ID */

  58. tid = nextThreadID();

  59. }

先看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. for (int j = 0; j < len; j++) {

  10. Entry e = parentTable[j];

  11. if (e != null) {

  12. ThreadLocal key = e.get();

  13. if (key != null) {

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

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

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

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

  18. h = nextIndex(h, len);

  19. table[h] = c;

  20. size++;

  21. }

  22. }

  23. }

  24. }

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

[java] view plain copy

  1. T childValue(T parentValue) {

  2. throw new UnsupportedOperationException();

  3. }

深入理解ThreadLocal(二)的更多相关文章

  1. 彻底理解ThreadLocal二

    首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各 ...

  2. 理解ThreadLocal(二)

    首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各 ...

  3. 【Java】深入理解ThreadLocal

    一.前言 要理解ThreadLocal,首先必须理解线程安全.线程可以看做是一个具有一定独立功能的处理过程,它是比进程更细度的单位.当程序以单线程运行的时候,我们不需要考虑线程安全.然而当一个进程中包 ...

  4. 理解ThreadLocal背后的概念

    介绍 我之前在任何场合都没有使用过thread local,因此没有注意到它,直到最近用到它的时候. 前提信息 线程可以理解为一个单独的进程,它有自己的调用栈.在java中每一个线程都有一个调用栈或者 ...

  5. 深入理解OOP(二):多态和继承(继承)

    本文是深入浅出OOP第二篇,主要说说继承的话题. 深入理解OOP(一):多态和继承(初期绑定和编译时多态) 深入理解OOP(二):多态和继承(继承) 深入理解OOP(三):多态和继承(动态绑定和运行时 ...

  6. C++ 中类的构造函数理解(二)

    C++ 中类的构造函数理解(二) 写在前面 上次的笔记中简要的探索了一下C++中类的构造函数的一些特性,这篇笔记将做进一步的探索.主要是复制构造函数的使用. 复制构造函数 复制构造函数也称拷贝构造函数 ...

  7. ppp 完全理解(二)【转】

    转自:https://blog.csdn.net/tianruxishui/article/details/44057717 ppp 完全理解(二) pppd 协议及代码分析 作者:李圳均 日期:20 ...

  8. 简单理解ThreadLocal原理和适用场景

    https://blog.csdn.net/qq_36632687/article/details/79551828?utm_source=blogkpcl2 参考文章: 正确理解ThreadLoca ...

  9. 理解ThreadLocal(之二)

    想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理.首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码 ...

随机推荐

  1. Openstack+Kubernetes+Docker微服务实践之路--Kubernetes

    经过几番折腾终于搞定Kubernetes了,我们要在Openstack上部署Kubernetes集群,使用最新工具Kubeadm来安装,由于不能直接访问Kubernetes的源,我们需要一台可以穿墙的 ...

  2. delphi 生成网卡MAC地址

    生成MAC地址的小工具: {*------------------------------------------------ 生成mac地址 @author @version 2015.7.2 20 ...

  3. PHP面向对象基础知识总结

    1.类的变量成员叫做“属性”,或者叫“字段”.“特征”,在本文档统一称为“属性”.2.属性中的变量可以初始化,但是初始化的值必须是常数,这里的常数是指php脚本在编译阶段时就为常数,而不是在编译阶段之 ...

  4. 架构探险——从零开始写Java Web框架》第二章照作

    沉下来慢慢看实现了. 越来越觉得可以和DJANGO作对比. package org.smart4j.chapter2.model; /** * Created by sahara on 2016/3/ ...

  5. ElasticSearch 概念解析

    索引(index) 1. 作为名词: 索引(index)指我们在 ES 中用来存放 doc 的一个库,类似数据库系统的 DB 概念. 2. 作为动词: 指把数据经过分析并存放到 ES 中形成索引(名词 ...

  6. 学习PHP时的一些总结(四)

    目录的基本操作: 在系统的每个目录下都有两个特殊的目录"."和".." , 分别指示当前目录和当前目录的父目录. dirname()  返回目录的名称 path ...

  7. 第10章 接口、继承与多态----Object类

    在Java中,所有的类都直接或间接继承了java.lang.Object类.Object类是比较特殊的类,它是所有类的父类,是Java类层中的最高层类.当创建一个类时,总是在继承,除非某个类已经指定要 ...

  8. nginx初级安装配置

    nginx初级安装配置 转自:(lykyl原创)http://www.cnblogs.com/lykyl/archive/2012/11/21/2781077.html 实验环境:系统 CENTOS5 ...

  9. JS对象属性命名规则

    JS标识符的命名规则,即变量的命名规则: 标识符只能由字母.数字.下划线和'$'组成 数字不可以作为标识符的首字符 对象属性的命名规则 通过[]操作符为对象添加属性时,属性名称可以是任何字符串(包括只 ...

  10. mybatis源码-解析配置文件(四-1)之配置文件Mapper解析(cache)

    目录 1. 简介 2. 解析 3 StrictMap 3.1 区别HashMap:键必须为String 3.2 区别HashMap:多了成员变量 name 3.3 区别HashMap:key 的处理多 ...