ThreadLocal的进化——InheritableThreadLocal
source link: https://segmentfault.com/a/1190000021284480
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
之前有介绍过 ThreadLocal,JDK 后来针对此做了一个升级版本 InheritableThreadLocal,今天就来好好介绍下。
<!-- more -->
为什么要升级
首先我们来想想,为什么要升级?这就要说起 ThreadLocal 的功能了。
我们知道,ThreadLocal 设计初衷是为了在多线程环境下,针对每一个线程能有一个自己的副本,这样可以在一定程度上解决多线程并发修改的问题。但是,我们可以在此基础上做一个拓展,比如 context
,我们可以利用 ThreadLocal 针对每一个线程都有一个自己的上下文,一般都是写成 ThreadLocal<Context>
,这样在这个线程上做的所有修改都可以被大家利用到。
此时设想一下,假如我们新建一个子线程,那这个子线程可以获取到父线程的 context
吗?理论上希望可以达成这样的效果,实际上呢?让我们看看:
public class ThreadLocalContext { private static ThreadLocal<Context> context = new ThreadLocal<>(); static class Context { String name; int value; } public static void main(String[] args) { Context context = new Context(); context.name = "mainName"; context.value = 10; ThreadLocalContext.context.set(context); Thread childThread = new Thread( new Runnable() { @Override public void run() { Context childContext = ThreadLocalContext.context.get(); System.out.println(childContext.name); System.out.println(childContext.value); } } ); childThread.start(); } }
运行 main 方法之后,直接在子线程中抛错,这样确实符合我们的预期,但如果我们想达到 子线程可以获取到父线程的 context
这样的效果该如何做呢?
首先想到的就是在生成子线程的时候,将父线程 ThreadLocal 里的值传给子线程。这样做虽然能达到效果,但过程比较繁杂,且代码侵入性强。
这个时候就可以用 InheritableThreadLocal
了。
什么是 InheritableThreadLocal
看源码
先让我们看看它的源码,大家不要怕,它的源码很少:
public class InheritableThreadLocal<T> extends ThreadLocal<T> { protected T childValue(T parentValue) { return parentValue; } ThreadLocalMap getMap(Thread t) { return t.inheritableThreadLocals; } void createMap(Thread t, T firstValue) { t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue); } }
首先它继承自 ThreadLocal,那么它其实就是 ThreadLocal 的一个拓展版本,接下来就是这三个方法,其实这三个方法在 ThreadLocal 都是有的,我们来看看:
T childValue(T parentValue) { throw new UnsupportedOperationException(); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
除了 childValue
方法在 ThreadLocal 中是抛出异常的,其余两个方法在两个类中都几乎是一样,只是针对的对象不同而已,但 threadLocals
和 inheritableThreadLocals
都是 ThreadLocal.ThreadLocalMap
类型,这个在之前的文章中有说过,就是一个 key 为 弱引用
的 Entry,这个倒不是重点。
我们再来看看 inheritableThreadLocals 是在何时被初始化的,从源码可以得知:
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { // 省略无关代码 ... Thread parent = currentThread(); ... // 省略无关代码 ... if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); ... }
当我们通过父线程调用 Thread 的构造方法生成一个子线程时,其构造方法最终会调用这个 init 方法。从这儿可以看出, inheritableThreadLocals 是来自于父线程的 inheritableThreadLocals,那这样也就解释了为什么 inheritableThreadLocals 支持在子线程中使用父线程中存储的变量。
如何使用
让我们还是回到上文提到的 context 的例子,用 InheritableThreadLocal 进行改造:
public class ThreadLocalContext { private static InheritableThreadLocal<Context> context = new InheritableThreadLocal<>(); static class Context { String name; int value; } public static void main(String[] args) { Context context = new Context(); context.name = "mainName"; context.value = 10; ThreadLocalContext.context.set(context); Thread childThread = new Thread( new Runnable() { @Override public void run() { Context childContext = ThreadLocalContext.context.get(); System.out.println(childContext.name); System.out.println(childContext.value); } } ); childThread.start(); } }
运行后,不仅没有抛出异常,而且在子线程中输出了父线程设置好的值。皆大欢喜!
总结
今天分享了 InheritableThreadLocal,主要是因为周三在携程的分享会上听到了别人谈了这方面的分享,主讲人讲了一个更加普遍的问题,如果我们用线程池提交任务的话,线程池中的线程在执行任务时,如何能够获得提交任务的线程的 context,这时就要用到阿里的开源组件 TTL,我会在之后进行介绍。
加入携程也有1个月了,虽然感受到大公司有不少的弊端,比如沟通难等,但也有不少的优点,比如技术分享会,虽然也是忙里偷闲去参加的,但有了更多和技术相关的可以学习和交流的机会,也挺好的。
有兴趣的话可以访问我的博客或者关注我的公众号、头条号,说不定会有意外的惊喜。
公众号:健程之道
Recommend
-
99
ThreadLocal介绍java.lang.ThreadLocal<T> 类是Java提供的用来保存线程本地变量的机制。说道线程本地变量很容易和和线程栈帧里的本地变量表联系起来。不过ThreadLocal的最普遍的用途是避免线程安全问题和框架代码实现模板模式。说道线程安全又要温习...
-
80
前言ThreadLocal很多同学都搞不懂是什么东西,可以用来干嘛。但面试时却又经常问到,所以这次我和大家一起学习ThreadLocal这个类。 下面我就以面试问答的形式学习我们的——ThreadLocal类(源码分析基于JDK8) 本文同步发布于简书 :ww
-
102
大家或多或少听说过ThreadLocal这个词,我们创建的对象默认是所有线程可以访问的,多线程并发修改对象时就会可能出现数据不一致的问题,使用ThreadLocal创建的对象只能被当前线程访问,每个线程保存一个对象的副本,在多线程操作时是线程安全的。
-
82
ThreadLocal的总结思考 殷庆荣@华为...
-
95
占小狼IP属地: 海南52018.01.21 10:33:09字数 1,433阅读 40,830 在面试环节中,考察"ThreadLocal"也是面试官的家常便...
-
51
java高并发系列第24篇文章。 环境:jdk1.8。 需要解决的问题 介绍ThreadLocal 介绍InheritableThreadLocal 需要解决的问题 我们还是以解决...
-
9
上一篇文章中,我们谈到了 InheritableThreadLocal,它解决了 ThreadLocal 针对父子线程无法共享 上下文 的问题。但我们可能听说过阿里的开源产品 TransmittableThreadLocal ,那么它又是做什么的呢? ...
-
34
前言 往期文章: Java 并发编程基础 ① - 线程
-
5
TransmittableThreadLocal(TTL) |
-
3
InheritableThreadLocal 在线程池中进行父子线程间消息传递出现消息丢失的解析 ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK