ThreadLocal和InheritableThreadLocal源码解析
ThreadLocal
作用
多线程访问同一个变量的时候会有线程安全的问题,ThreadLocal会把每一个线程访问的变量变成这个线程私有的变量,从而避免了线程安全的问题。
使用代码
ps: 其中线程休眠,是为了让其他线程有机会在赋值之后再操作ThreadLocal;
public class ThreadLocalTest { public static void main(String[] args) { ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); for (int x = 1; x <= 5; x++) { final int index = x; Thread thread = new Thread(() -> { threadLocal.set(index); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " : " + threadLocal.get()); threadLocal.remove(); }); thread.start(); } } }
-
运行结果:
可以验证,ThreadLocal保证了变量是被每一个线程肚子拥有的,从而避免了线程安全的问题。
原理
- 下面是源码中ThreadLocal中的set方法
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
我们可以看到他用当前线程做key获取到了一个map,而这个map就是Thread线程类本身维护的一个变量
所以,其实,ThreadLocal的变量是存储在每个线程本身的threadLocals map变量中,这样也是很合理的,每个线程都有自己的容器来存储自己线程特有的变量,而这个map的key就是ThreadLocal的引用this,这样就可以保证,每个线程可以存储多个ThreadLocal的变量。
当然这个threadLocals不是在县城创建的时候初始化的,是在ThreadLocal第一次get或者set的时候检测为空的话就会进行初始化。
- 下面是源码中ThreadLocal中的get方法
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(); }
- setInitialValue方法源码
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
如果map不为空的话,那么就用ThreadLocal的引用this,去拿当前ThreadLocal存储的值,如果这时候线程中的threadLocals变量还没有初始化就要走setInitialValue方法。这个方法,会将threadLocals变量进行初始化,并且用ThreadLocal的引用this当key,null做值,进行一个初始化,并返回null。
- remove方法
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
如果使用了ThreadLocal向当前的线程里面set了值的话,如果线程不结束,这个值是不会消失的,如果你想删除这个值,就可以调用remove方法,其实就是把这个Entry从map中删除而已。
ThreadLocal的问题
不支持继承性
public class ThreadLocalTest { public static void main(String[] args) { ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); threadLocal.set(111); new Thread(() -> System.out.println(threadLocal.get())).start(); } }
上面这理所当然的取不出来值,打印结果为null,因为子线程的threadLocals并没有存主线程的值,而要想解决这个问题,就需要认识一个新的工具InheritableThreadLocal。
InheritableThreadLocal
上文说了,InheritableThreadLocal是为了解决ThreadLocal不支持继承性而产生的,那我们先看一下使用了InheritableThreadLocal的效果。
public class ThreadLocalTest { public static void main(String[] args) { InheritableThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>(); threadLocal.set(111); new Thread(() -> System.out.println(threadLocal.get())).start(); } }
-
运行结果
果不其然,我们获取到了主线程set到的111。
原理解析
打开InheritableThreadLocal类的源码,我们可以看到他是继承了ThreadLocal类,并且仅仅重写了三个方法
其中inheritableThreadLocals肯定引起了你的注意,这个变量和threadLocals变量一样,也是每一个线程私有的一个map,name这个的作用是什么的?为什么InheritableThreadLocals将返回的map变成了InheritableThreadLocals就可以实现继承性了呢,我们看一下Thread类的源码。我们在线程的初始化方法init中看到了
如果父线程的InheritableThreadLocals不为空,就会复制给子线程,这样,父线程的变量当然就可以被子线程看到啦,因为这里进行了同步,所以inheritableThreadLocals可以实现线程的继承性
本文地址:https://blog.csdn.net/qq_36929361/article/details/108245175
上一篇: 耳穴神能治疗经衰弱和失眠
下一篇: 农夫与猴子的笑话故事
推荐阅读
-
Spring MVC源码(三) ----- @RequestBody和@ResponseBody原理解析
-
Laravel源码解析之路由的使用和示例详解
-
Mybaits 源码解析 (十一)----- 设计模式精妙使用:静态代理和动态代理结合使用:@MapperScan将Mapper接口生成代理注入到Spring
-
Mybaits 源码解析 (十二)----- Mybatis的事务如何被Spring管理?Mybatis和Spring事务中用的Connection是同一个吗?
-
netty源码解解析(4.0)-23 ByteBuf内存管理:分配和释放
-
Mybaits 源码解析 (九)----- 全网最详细,没有之一:一级缓存和二级缓存源码分析
-
Android源码解析之应用程序框架层和系统运行库层日志系统分析
-
jQuery 源码解析(二十五) DOM操作模块 html和text方法的区别
-
从vue源码解析Vue.set()和this.$set()
-
Android ABC Jetpack学习之一文学会Navigation(附源码解析和使用封装)