当前位置:首页 > 科技  > 软件

深度解析Java Thread Locals工作原理

来源: 责编: 时间:2024-05-29 08:59:47 196观看
导读一、前言在Java中,线程本地变量的作用域是整个线程。这意味着这种变量可以从线程中的任何位置设置,并可以从同一线程的任何位置访问。从一个线程设置的值对另一个线程是不可访问的。我们应该知道,Java中有两种类型的线程

一、前言

在Java中,线程本地变量的作用域是整个线程。这意味着这种变量可以从线程中的任何位置设置,并可以从同一线程的任何位置访问。从一个线程设置的值对另一个线程是不可访问的。UQz28资讯网——每日最新资讯28at.com

我们应该知道,Java中有两种类型的线程本地类——ThreadLocal和InheritableThreadLocal。让我们看看这两者之间的区别。UQz28资讯网——每日最新资讯28at.com

二、ThreadLocal类

下面是一个如何声明线程本地变量的示例。变量user是一个ThreadLocal变量,它保存一个User类型的变量(类或接口)。请注意,这里变量被声明为public和static,以便user变量可以从代码中的任何位置访问。UQz28资讯网——每日最新资讯28at.com

// 声明一个线程本地变量userpublic static final ThreadLocal user                      = new ThreadLocal<>();

下面是我们如何为一个线程设置和获取user。该示例显示user变量被设置为用户对象bob。在同一线程中,如果我们调用get()方法,就会检索到用户bob。UQz28资讯网——每日最新资讯28at.com

// 设置调用线程的user值user.set(new User("bob"));// 获取调用线程的user值User requestUser = user.get();

请注意,即使user变量对整个代码库是可访问的,但set(..)方法确保传递给它的用户对象与calling线程相关联。get()方法也会检索与calling线程相关联的用户对象,这就是为什么当在不同线程上调用get()方法时,它不会检索到bob而是其他用户(或null)的原因。每个Java线程都与一个包含该线程所有设置的线程本地变量的ThreadLocal映射相关联。UQz28资讯网——每日最新资讯28at.com

如果我们在未设置任何值的情况下调用get()方法,该方法将简单地返回null。UQz28资讯网——每日最新资讯28at.com

然而,你可以创建一个带有Lambda Supplier的线程本地对象,它将返回一个初始的用户对象。下面的示例显示了一个Supplier,它返回一个名为anonymous的用户。因此,如果在未设置值的情况下调用ThreadLocal上的get()方法,则会调用Supplier上的get()方法,并将该值设置为用户的初始值。UQz28资讯网——每日最新资讯28at.com

// 声明一个带有Supplier的线程本地变量userpublic static ThreadLocal user           = ThreadLocal.withInitial(                () -> new User("anonymous"))// 返回AnonymousUser requestUser = user.get();

你也可以通过简单地调用remove()方法来删除之前设置的值,如下所示。UQz28资讯网——每日最新资讯28at.com

// 删除调用线程的user值user.remove();

该方法基本上会删除与线程相关联的用户对象。更重要的是,其他线程不会受到此操作的影响。UQz28资讯网——每日最新资讯28at.com

如果我们以图表形式来可视化线程本地变量,它看起来会像这样。请注意,两个线程的user变量指向的是不同的用户对象。UQz28资讯网——每日最新资讯28at.com

图片图片UQz28资讯网——每日最新资讯28at.com

三、ThreadLocal和子线程

到目前为止,我们的讨论主要集中在单个Java线程上。如果一个Java线程启动了一个新的子线程,子线程会自动能够访问父线程中定义的线程本地变量吗?UQz28资讯网——每日最新资讯28at.com

答案是否定的!子线程无法访问父线程的线程本地变量,这是有充分理由的。如果能够访问,那么存储在线程本地变量中的对象就必须为线程安全而编写,因为多个线程能够访问同一个用户对象。这是Java工程师做出的一个很好的默认设计决策。UQz28资讯网——每日最新资讯28at.com

但是,在某些情况下,这种访问是有用的。想象一个Web应用程序的场景,许多用户正在访问应用程序。一个单独的Java线程与整个请求处理过程中的用户相关联,你可以想象用户对象存储在线程的线程本地对象中(这是许多应用服务器和框架如Spring Boot所做的)。但是,你可能希望生成的子线程也能访问这些用户信息。UQz28资讯网——每日最新资讯28at.com

对于这种场景,Java提供了另一个名为InheritableThreadLocal的类。UQz28资讯网——每日最新资讯28at.com

四、InheritableThreadLocal类

使用这个类的语法与ThreadLocal类基本相同。下面的示例显示了InheritableThreadLocal类的相应方法。UQz28资讯网——每日最新资讯28at.com

// 声明一个可继承的线程本地变量userpublic static final InheritableThreadLocal user            = new InheritableThreadLocal<>();// 设置调用线程的user值user.set(new User("bob"));// 获取调用线程的user值User requestUser = user.get();// 删除调用线程的user值user.remove();

与Thread Local映射一样,每个线程也有一个用于可继承线程本地变量的映射。这里的关键区别是,当创建子线程时,子线程的可继承线程本地映射会从父线程克隆。因此,可继承线程本地变量对子线程也是可访问的。UQz28资讯网——每日最新资讯28at.com

如果我们以图表形式可视化可继承线程本地变量,它看起来会像这样。可以看到,InheritableThreadLocal映射是从父线程克隆而来的。UQz28资讯网——每日最新资讯28at.com

图片图片UQz28资讯网——每日最新资讯28at.com

五、注意事项

正如上图所清楚显示的,可继承线程本地变量所见到的优势也是一种缺点。默认情况下,当创建子线程时,可继承线程本地映射也会被克隆。但是你也可以看到,user指向父线程和子线程中相同的用户对象。UQz28资讯网——每日最新资讯28at.com

这意味着用户对象可以从多个线程访问,因此需要以线程安全的方式编写。换句话说,如果使用InheritableThreadLocal类,之前ThreadLocal类的线程安全性就会丢失。这对于你的设计可能是完全有效的。UQz28资讯网——每日最新资讯28at.com

然而,还有一种更安全的方法。我们可以在创建InheritableThreadLocal时指定一个childValue(..)方法。事实上,在下面的示例中,我们同时指定了一个初始值和一个子值。UQz28资讯网——每日最新资讯28at.com

public static final InheritableThreadLocal user                    = new InheritableThreadLocal<>() {   @Override   protected User initialValue() {       return new User("anonymous");    }   @Override   protected User childValue(User parentValue) {       return new User(parentValue.getId());    }};

在这种更改下,当Inheritable Thread Local映射被克隆时,与子线程关联的值将使用childValue(..)方法设置,该方法通过传递父线程的值来初始化每个Inheritable Thread Local。由于我们是从childValue(..)方法创建了一个新的对象,因此用户对象不会在父线程和子线程之间共享。通过这一改变,我们恢复了线程安全性,同时也能以只读的方式访问用户对象(通过有效地创建一个副本)。UQz28资讯网——每日最新资讯28at.com

同样,如果我们以图表形式可视化Inheritable Thread Locals,它看起来会像这样。很明显,现在用户分别指向父线程和子线程中的不同用户对象。UQz28资讯网——每日最新资讯28at.com

图片图片UQz28资讯网——每日最新资讯28at.com

希望这能让你对Java Thread Local变量有一个较好的理解,以及它们在应用程序中如何使用。UQz28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-91379-0.html深度解析Java Thread Locals工作原理

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: Git 不要只会 Pull 和 Push,这五条提高效率的命令得掌握!

下一篇: 畅爽领先游戏体验 玩手游就选三星 S24 Ultra

标签:
  • 热门焦点
  • Find N3入网:最高支持16+1TB

    OPPO将于近期登场的Find N3折叠屏目前已经正式入网,型号为PHN110。本次Find N3在外观方面相比前两代有很大的变化,不再是小号的横向折叠屏,而是跟别的厂商一样采用了较为常见的
  • Redmi Buds 4开箱简评:才199还有降噪 可以无脑入

    在上个月举办的Redmi Note11T Pro系列新机发布会上,除了两款手机新品之外,Redmi还带来了两款TWS真无线蓝牙耳机产品,Redmi Buds 4和Redmi Buds 4 Pro,此前我们在Redmi Note11T
  • 六大权益!华为8月服务日开启:手机免费贴膜、维修免人工费

    8月5日消息,一年一度的华为开发者大会2023(Together)日前在松山湖拉开帷幕,与此同时,华为8月服务日也式开启,到店可享六大专属权益。华为用户可在华为商城Ap
  • JavaScript 混淆及反混淆代码工具

    介绍在我们开始学习反混淆之前,我们首先要了解一下代码混淆。如果不了解代码是如何混淆的,我们可能无法成功对代码进行反混淆,尤其是使用自定义混淆器对其进行混淆时。什么是混
  • Rust中的高吞吐量流处理

    作者 | Noz编译 | 王瑞平本篇文章主要介绍了Rust中流处理的概念、方法和优化。作者不仅介绍了流处理的基本概念以及Rust中常用的流处理库,还使用这些库实现了一个流处理程序
  • 一文搞定Java NIO,以及各种奇葩流

    大家好,我是哪吒。很多朋友问我,如何才能学好IO流,对各种流的概念,云里雾里的,不求甚解。用到的时候,现百度,功能虽然实现了,但是为什么用这个?不知道。更别说效率问题了~下次再遇到,
  • 雅柏威士忌多款单品价格大跌,泥煤顶流也不香了?

    来源 | 烈酒商业观察编 | 肖海林今年以来,威士忌市场开始出现了降温迹象,越来越多不断暴涨的网红威士忌也开始悄然回归市场理性。近日,LVMH集团旗下苏格兰威士忌品牌雅柏(Ardbeg
  • 2天涨粉255万,又一赛道在抖音爆火

    来源:运营研究社作者 | 张知白编辑 | 杨佩汶设计 | 晏谈梦洁这个暑期,旅游赛道彻底火了:有的「地方」火了&mdash;&mdash;贵州村超旅游收入 1 个月超过 12 亿;有的「博主」火了&m
  • 支持aptX Lossless无损传输 iQOO TWS 1赛道版发布限时优惠价369元

    2023年7月4日,“无损音质,声动人心”iQOO TWS 1正式发布,支持aptX Lossless无损传输,限时优惠价369元。iQOO TWS 1耳机率先支持端到端aptX Lossless无
Top