当前位置: 首页 > 面试题库 >

Java的ThreadLocal如何在后台实现?

曹泉
2023-03-14
问题内容

ThreadLocal如何实现?它是用Java实现的(使用从ThreadID到对象的一些并发映射),还是使用一些JVM挂钩来更有效地实现它?


问题答案:

这里的所有答案都是正确的,但是有些令人失望,因为它们多少掩盖了聪明ThreadLocal的实现是多么的明智。我只是在寻找源代码,ThreadLocal并且对它的实现方式印象深刻。

天真的实现

如果我要求您ThreadLocal<T>在javadoc中描述的给定API的基础上实现一个类,该怎么办?最初的实现可能是ConcurrentHashMap<Thread,T>使用Thread.currentThread()作为其密钥。这样会很好地工作,但确实有一些缺点。

  • 线程争用- ConcurrentHashMap是一个非常聪明的类,但是它最终仍必须处理防止多个线程以任何方式破坏它,并且如果不同的线程有规律地命中它,将会降低速度。
  • 即使在线程完成并且可以进行GC处理后,它仍永久保持指向线程和对象的指针。

GC友好的实现

好的,再试一次,让我们使用弱引用来处理垃圾回收问题。处理WeakReferences可能会造成混淆,但是使用像这样构建的地图应该足够了:

 Collections.synchronizedMap(new WeakHashMap<Thread, T>())

或者,如果我们使用的是番石榴(应该是!):

new MapMaker().weakKeys().makeMap()

这意味着一旦没有其他人抓住线程(暗示线程已完成),就可以对键/值进行垃圾收集,这是一种改进,但仍无法解决线程争用问题,这意味着到目前为止,我们ThreadLocal还不是全部令人赞叹的一堂课。此外,如果有人决定在Thread完成后保留对象,那么就永远不会对它们进行GC处理,因此即使我们的对象现在在技术上无法到达,也不会对其进行GC处理。

聪明的实现

我们一直在考虑ThreadLocal将线程映射为值,但是也许这实际上并不是正确的思考方式。与其将其视为从Threads到每个ThreadLocal对象中的值的映射,不如将其视为ThreadLocal对象到
每个Thread中的
值的映射怎么办?如果每个线程都存储了该映射,而ThreadLocal仅提供了该映射的一个不错的接口,我们可以避免以前实现中的所有问题。

一个实现看起来像这样:

// called for each thread, and updated by the ThreadLocal instance
new WeakHashMap<ThreadLocal,T>()

此处无需担心并发性,因为只有一个线程将访问此映射。

Java开发人员在这里比我们有一个主要优势-他们可以直接html" target="_blank">开发Thread类并向其添加字段和操作,而这正是他们所做的。

其中java.lang.Thread有以下几行:

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

正如评论所暗示的,确实是ThreadLocal对象为此跟踪的所有值的私有包映射Thread。的实现ThreadLocalMap不是WeakHashMap,而是遵循相同的基本协定,包括通过弱引用来持有其密钥。

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

ThreadLocal.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;
}

本质上, 在此线程中
使用地图可以容纳我们所有的ThreadLocal对象。这样,我们永远不必担心其他线程中的值(ThreadLocal字面上只能访问当前线程中的值),因此没有并发问题。此外,一旦Thread完成,其映射将自动进行GC处理,并且将清除所有本地对象。即使将Thread其保留,ThreadLocal对象也会被弱引用保留,并且一旦ThreadLocal对象超出范围,就可以将其清除。

不用说,这个实现给我留下了深刻的印象,它很好地解决了很多并发问题(可以利用作为核心Java的一部分,但这是可以原谅的,因为它是一个非常聪明的类),并且允许快速和对仅一次需要一个线程访问的对象的线程安全访问。

tl; dr ThreadLocal的实现非常酷,并且比您乍看之下要快/聪明得多。

如果您喜欢这个答案,您可能也会喜欢我(不那么详细)的讨论ThreadLocalRandom

Thread/ ThreadLocal代码段摘自Oracle / OpenJDK的Java
8实现。



 类似资料:
  • 问题内容: Java的switch语句如何在后台运行?它如何将使用的变量的值与案例部分给出的值进行比较?它使用还是,还是完全其他? 我主要对1.7之前的版本感兴趣。 问题答案: 都不行 它使用JVM指令,这实际上是一个表查找。查看以下示例的字节码:

  • 问题内容: 我在创建ThreadLocal并使用new ThreadLocal对其进行初始化时遇到问题。问题是,从概念上讲,我真的只是想要一个持久列表,该列表可以延续线程的寿命,但是我不知道是否有一种方法可以在Java中初始化每个线程的内容。 例如,我想要的是这样的: 这样就可以为每个线程初始化它。我知道我可以这样做: 但我真的不想在每次使用时都进行检查。我能在这里做得更好吗? 问题答案: 您只需

  • 2022-12-08 (1h10分钟) 主要是问了八股,最后是手撕一道比较简单的算法题改编 八股 ArrayList和LinkedList的区别 LinkedList底层是怎样的链表 HashMap底层实现,1.7和1.8的区别 为什么长度是8的时候转变为红黑树 ConcurrentHashMap底层是如何进行并发控制的 volatile底层实现 什么场景下会出现总线风暴 jvm底层是如何实现CA

  • 我要研究一下在一天24小时中,哪一个键在哪一个小时打得最多?后来,我会研究在一天24小时中,哪个词在哪个小时使用得最多? null

  • 本文向大家介绍Java如何实现简单后台访问并获取IP,包括了Java如何实现简单后台访问并获取IP的使用技巧和注意事项,需要的朋友参考一下 后台服务端 客户端 后台接收的信息 客户端接收的信息 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。

  • ThreadLocal类用于创建线程局部变量,这些变量只能由同一线程读取和写入。 例如,如果两个线程正在访问引用相同threadLocal变量的代码,则每个线程都不会看到由其他线程完成的对threadLocal变量的任何修改。 ThreadLocal方法 以下是ThreadLocal类中可用的重要方法列表。 Sr.No. 方法和描述 1 public T get() 返回当前线程的此线程局部变量副