Java 的 ThreadLocal 底层是如何实现的?

2024-01-08

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


这里的所有答案都是正确的,但有点令人失望,因为它们在某种程度上掩盖了如何聪明ThreadLocal的实现是.我只是在看源代码ThreadLocal http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/lang/ThreadLocal.java并对它的实施方式印象深刻。

幼稚的实施

如果我要求你实施一个ThreadLocal<T>给定 javadoc 中描述的 API,您会做什么?初步实施可能是ConcurrentHashMap<Thread,T> using Thread.currentThread()作为它的关键。这将工作得相当好,但也有一些缺点。

  • 线程争用 -ConcurrentHashMap是一个非常聪明的类,但它最终仍然必须处理防止多个线程以任何方式破坏它,并且如果不同的线程定期命中它,就会出现速度减慢的问题。
  • 永久保留指向线程和对象的指针,即使线程已完成并且可以被 GC 后也是如此。

GC 友好的实现

好的,再试一次,让我们使用以下方法来处理垃圾收集问题弱引用 http://en.wikipedia.org/wiki/Weak_reference。处理 WeakReferences 可能会令人困惑,但使用如下构建的映射应该足够了:

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

或者如果我们使用Guava http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/MapMaker.html(我们应该如此!):

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

这意味着一旦没有其他人持有该线程(意味着它已完成),键/值就可以被垃圾收集,这是一种改进,但仍然没有解决线程争用问题,这意味着到目前为止我们的ThreadLocal类不是那么令人惊奇。此外,如果有人决定坚持下去Thread对象完成后,它们永远不会被GC,因此我们的对象也不会被GC,即使它们现在在技术上是无法访问的。

巧妙的实施

我们一直在思考ThreadLocal作为线程到值的映射,但这实际上可能不是正确的思考方式。与其将其视为从 Threads 到每个 ThreadLocal 对象中的值的映射,不如将其视为 ThreadLocal 对象到值的映射会怎样?在每个线程中?如果每个线程都存储映射,并且 ThreadLocal 只是为该映射提供一个很好的接口,那么我们就可以避免以前实现的所有问题。

一个实现看起来像这样:

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

这里无需担心并发性,因为只有一个线程会访问该映射。

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

In java.lang.Thread http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/tip/src/share/classes/java/lang/Thread.java有以下几行:

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

And 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 https://stackoverflow.com/a/41760223/113632.

Thread/ThreadLocal code snippets taken from Oracle/OpenJDK's implementation of Java 8 http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/jdk8-b132.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 的 ThreadLocal 底层是如何实现的? 的相关文章

  • Java 7 默认语言环境

    我刚刚安装了 jre7 我很惊讶地发现我的默认区域设置现在是 en US 对于jre6 它是de CH 与jre7有什么不同 默认区域设置不再是操作系统之一吗 顺便说一句 我使用的是Windows7 谢谢你的回答 编辑 我已经看到了语言环境
  • Oracle Java 教程 - 回答问题时可能出现错误

    我是 Java 新手 正在阅读 Oracle 教程 每个部分之后都有问题和答案 我不明白一个答案中的一句话 见下面的粗体线 来源是https docs oracle com javase tutorial java javaOO QandE
  • 当路径的点超出视野时,Android Canvas 不会绘制路径

    我在绘制路径时遇到了 Android Canvas 的一些问题 我的情况是 我有一个相对布局工作 如地图视图 不使用 google api 或类似的东西 我必须在该视图上绘制一条路径 canvas drawPath polyPath bor
  • HAProxy SSL终止+客户端证书验证+curl/java客户端

    我希望使用我自己的自签名证书在 HAProxy 上进行 SSL 终止 并使用我创建的客户端证书验证客户端访问 我通过以下方式创建服务器 也是 CA 证书 openssl genrsa out ca key 1024 openssl req
  • 如何将jscrollpane添加到jframe?

    我有以下源代码 有人可以给我建议如何将 jscrollpane 添加到 jframe 上吗 我尝试了几次将其添加到 jframe 但没有任何进展 它甚至没有显示 public class Form3 JFrame jframe new JF
  • 将SQL数据引入jquery availabletag

    我正在尝试制作自动完成文本框 但如何将 SQL 数据包含到 jquery 可用标记并循环它 我无法根据以下代码执行该功能 任何帮助 将不胜感激 谢谢 这是我的预期输出 预期结果演示 http jsfiddle net VvETA 71 jq
  • 为自定义驱动程序创建 GraphicsDevice

    我正在开发一个在嵌入式系统中使用 Java 的项目 我有用于屏幕和触摸输入的驱动程序 以及用于文本输入的虚拟键盘 我的屏幕驱动程序有一个Graphics2D您可以绘制的对象和repaint Rectangle 更新方法 类似地 触摸驱动器能
  • 为什么 MOVE CURSOR 在 OS X Mountain Lion 上不显示?

    我正在做一个项目 想看看 Swing 提供的每个光标是什么样子的 public class Test public static void main String args JFrame frame new JFrame frame set
  • Spring数据中的本机查询连接

    我有课 Entity public class User Id Long id String name ManyToMany List
  • 如何通过注解用try-catch包装方法?

    如果应该在方法调用中忽略异常 则可以编写以下内容 public void addEntryIfPresent String key Dto dto try Map
  • 从直方图计算平均值和百分位数?

    我编写了一个计时器 可以测量任何多线程应用程序中特定代码的性能 在下面的计时器中 它还会在地图中填充花费了 x 毫秒的调用次数 我将使用这张图作为我的直方图的一部分来进行进一步的分析 例如调用花费了这么多毫秒的百分比等等 public st
  • 我们如何测试包私有类?

    我正在看书Effective Java in Item 13 Minimize the accessibility of classes and members 它提到 为了方便测试 您可能想让类 接口或成员更易于访问 这在某种程度上是好的
  • 如何通过 Android 按钮单击运行单独的应用程序

    我尝试在 Android 应用程序中添加两个按钮 以从单独的两个应用程序订单系统和库存系统中选择一个应用程序 如图所示 我已将这两个应用程序实现为两个单独的 Android 项目 当我尝试运行此应用程序时 它会出现直到正确选择窗口 但是当按
  • Play.application() 的替代方案是什么

    我是 Play 框架的新手 我想读取conf文件夹中的一个文件 所以我用了Play application classloader getResources Data json nextElement getFile 但我知道 play P
  • Java - 从 XML 文件读取注释

    我必须从 XML 文件中提取注释 我找不到使用 JDOM 或其他东西来让它们使用的方法 目前我使用 Regex 和 FileReader 但我不认为这是正确的方法 您可以使用 JDOM 之类的东西从 XML 文件中获取注释吗 或者它仅限于元
  • 使用Java绘制维恩图

    我正在尝试根据给定的布尔方程绘制维恩图 例如 a AND b AND c我想在 Android 手机上执行此操作 因此我需要找到一种使用 Java 来执行此操作的方法 我找到了一个完美的小部件 它可以完成我在这方面寻找的一切布尔代数计算器
  • 无需登录即可直接从 Alfresco 访问文件/内容

    我的场景是这样的 我有一个使用 ALFRESCO CMS 来显示文件或图像的 Web 应用程序 我正在做的是在 Java servlet 中使用用户名和密码登录 alfresco 并且我可以获得该登录的票证 但我无法使用该票证直接从浏览器访
  • 每个客户端一个线程与线程服务器的排队线程模型之间的相对优点?

    假设我们正在构建一个线程服务器 旨在在具有四个核心的系统上运行 我能想到的两种线程管理方案是每个客户端连接一个线程和一个排队系统 正如第一个系统的名称所暗示的那样 我们将为每个连接到服务器的客户端生成一个线程 假设一个线程始终专用于程序的主
  • 使用 Java https 上传到 Imgur v3 错误

    我目前正在尝试使用他们当前的 API v3 上传到 imgur 但是我不断收到错误 错误 javax net ssl SSLException 证书中的主机名不匹配 api imgur com imgur com OR imgur com
  • 使用 JFreeChart 为两个系列设置不同的 y 轴

    我正在使用 JFreeChart 使用折线图绘制两个数据系列 XYSeries 复杂的因素是 其中一个数据系列的 y 值通常远高于第二个数据系列的 y 值 假设第一个系列的 y 值约为数百万数量级 而第二个数据系列的 y 值约为数百万数量级

随机推荐