简体   繁体   English

是否可以为Java虚拟线程的载体线程创建一个ThreadLocal?

[英]Is it possible to create a ThreadLocal for the carrier thread of a Java virtual thread?

JEP-425: Virtual Threads states that a new virtual thread "should be created for every application task" and makes twice a reference to the possibility of having "millions" of virtual threads running in the JVM. JEP-425:虚拟线程指出“应该为每个应用程序任务创建一个新的虚拟线程”,并两次提到在 JVM 中运行“数百万”虚拟线程的可能性。

The same JEP implies that each virtual thread will have access to its own thread-local value:相同的 JEP 意味着每个虚拟线程都可以访问自己的线程局部值:

Virtual threads support thread-local variables [...] just like platform threads, so they can run existing code that uses thread locals.虚拟线程支持线程局部变量 [...] 就像平台线程一样,因此它们可以运行使用线程局部变量的现有代码。

Thread locals are many times used for the purpose of caching an object that is not thread-safe and expensive to create.线程局部变量多次用于缓存 object 的目的,它不是线程安全的并且创建起来很昂贵。 The JEP warns: JEP 警告说:

However, because virtual threads can be very numerous, use thread locals after careful consideration.但是,由于虚拟线程可能非常多,请在仔细考虑后使用线程局部变量。

Numerous indeed!确实很多! Especially given how virtual threads are not pooled (or at least shouldn't be ).特别是考虑到虚拟线程是如何不合并的(或者至少不应该合并)。 As representative of a short-lived task, using thread locals in a virtual thread for the purpose of caching an expensive object seems to be borderline void of meaning.作为短期任务的代表,在虚拟线程中使用线程局部变量来缓存昂贵的 object 似乎毫无意义。 Unless!除非! We can from a virtual thread create and access thread locals bound to its carrier thread我们可以从虚拟线程创建和访问绑定到其载体线程的线程局部变量

For clarification, I would like to go from something like this (which would have been perfectly acceptable when using only native threads capped to the size of a pool, but this is clearly no longer a very effective caching mechanism when running millions of virtual threads continuously re-created:为了澄清,我想从这样的事情到 go(当只使用限制为池大小的本机线程时,这是完全可以接受的,但是当连续运行数百万个虚拟线程时,这显然不再是一种非常有效的缓存机制重新创建:

static final ThreadLocal<DateFormat> CACHED = ThreadLocal.withInitial(DateFormat::getInstance);

To this (alas this class is not part of the public API):为此(唉,这个 class 不是公共 API 的一部分):

static final ThreadLocal<DateFormat> CACHED = new jdk.internal.misc.CarrierThreadLocal();
// CACHED.set(...)

Before we even get there.在我们到达那里之前。 One must ask, is this a safe practice?有人必须问,这是一种安全的做法吗?

Well, as far as I have understood virtual threads correctly, they are merely logical stages executed on a platform thread (aka. the "carrier thread") with the ability to unmount instead of being blocked waiting.好吧,就我对虚拟线程的正确理解而言,它们只是在平台线程(又名“载体线程”)上执行的逻辑阶段,具有卸载而不是被阻塞等待的能力。 So I am assuming - please correct me if I am wrong - that 1) the virtual thread will never be interleaved by another virtual thread on the same carrier thread or rescheduled on another carrier thread unless the code would have blocked otherwise and therefore, if 2) the operation we invoke on the cached object never blocks, then the task/virtual thread will simply run from start to finish on the same carrier and so yes, it would be safe to cache the object on a platform thread-local.所以我假设 - 如果我错了请纠正我 - 1)虚拟线程永远不会被同一载体线程上的另一个虚拟线程交错或重新安排在另一个载体线程上,除非代码会被阻塞,因此,如果 2 ) 我们在缓存的 object 上调用的操作永远不会阻塞,然后任务/虚拟线程将简单地从头到尾在同一载体上运行,所以是的,将 object 缓存在平台线程本地上是安全的。

With the risk of answering my own question, JEP-425 indicates this is not possible:冒着回答我自己问题的风险,JEP-425 表明这是不可能的:

Thread-local variables of the carrier are unavailable to the virtual thread, and vice-versa.载体的线程局部变量对虚拟线程不可用,反之亦然。

I could not find a public API to get the carrier thread or allocate thread locals explicitly on a platform thread [from a virtual thread], but that's not to say my humble research is definitive.我找不到公共 API 来获取载体线程或在平台线程 [从虚拟线程] 上显式分配线程局部变量,但这并不是说我的拙劣研究是确定的。 Maybe there is a way?也许有办法?

Then I read JEP-429: Scoped Values which at first glance seems to be a stab by the Java Gods to get rid of the ThreadLocal altogether, or at least provide an alternative for virtual threads.然后我读了JEP-429:Scoped Values ,乍一看似乎是 Java Gods 为了完全摆脱ThreadLocal ,或者至少为虚拟线程提供了一个替代方案。 In fact, the JEP uses verbiage such as "migration to scoped values" and says they are "preferred to thread-local variables, especially when using large numbers of virtual threads".事实上,JEP 使用诸如“迁移到作用域值”之类的措辞,并表示它们“优先于线程局部变量,尤其是在使用大量虚拟线程时”。

For all the use cases discussed in the JEP, I can only agree.对于 JEP 中讨论的所有用例,我只能同意。 But towards the bottom of this document, we also find this:但是在本文档的底部,我们还发现了这一点:

There are a few scenarios that favor thread-local variables.有一些场景支持线程局部变量。 An example is caching objects that are expensive to create and use, such as instances of java.text.DateFormat.一个示例是缓存创建和使用成本很高的对象,例如 java.text.DateFormat 的实例。 Notoriously, a DateFormat object is mutable, so it cannot be shared between threads without synchronization.众所周知,DateFormat object 是可变的,因此它不能在没有同步的情况下在线程之间共享。 Giving each thread its own DateFormat object, via a thread-local variable that persists for the lifetime of the thread, is often a practical approach.通过在线程的生命周期内持续存在的线程局部变量,为每个线程提供自己的 DateFormat object 通常是一种实用的方法。

In light of what was discussed earlier, using a thread-local may be "practical" but not very ideal.根据前面讨论的内容,使用局部线程可能“实用”但不是很理想。 In fact, JEP-429 itself actually started off with a very telling remark: "if each of a million virtual threads has mutable thread-local variables, the memory footprint may be significant".事实上,JEP-429 本身实际上是从一个非常有说服力的评论开始的:“如果一百万个虚拟线程中的每一个都有可变的线程局部变量,那么 memory 的足迹可能会很大”。

To summarize:总结一下:

Have you found a way to allocate thread locals on a carrier thread from the virtual thread?您是否找到了一种从虚拟线程在载体线程上分配线程局部变量的方法?

If not, is it safe to say that for applications using virtual threads, the practice of caching objects in a thread-local is dead and one will have to implement/use a different approach such as a concurrent cache/map/pool/whatever?如果不是,可以肯定地说,对于使用虚拟线程的应用程序,在线程本地缓存对象的做法已经过时,人们将不得不实现/使用不同的方法,例如并发缓存/映射/池/等等?

Honestly, I am not sure if I understand your question right.老实说,我不确定我是否理解你的问题。 JEP 429 states the following: JEP 429声明如下:

  • "Every thread-local variable is mutable" and "The thread-local variable serves as a kind of hidden method argument" but it is quite clear that " this can lead to spaghetti-like data flow" and much discipline and some engineering effort are recommended. “每个线程局部变量都是可变的”和“线程局部变量用作一种隐藏的方法参数”但是很明显“这会导致像意大利面条一样的数据流”并且很多纪律和一些工程工作是推荐的。 Some best practices or more restrictions might be helpful...一些最佳实践或更多限制可能会有所帮助......
  • "... if each of a million virtual threads has mutable thread-local variables, the memory footprint may be significant." “......如果一百万个虚拟线程中的每一个都有可变的线程局部变量,那么 memory 的足迹可能会很大。” This raises the question of how an implementation of an algorithm should be designed, taking advantage of the parallelization with virtual threads.这就提出了一个问题,即应该如何设计算法的实现,利用虚拟线程的并行化。
  • "The Java Platform should provide a way to maintain immutable and inheritable per-thread data for thousands or millions of virtual threads." “Java 平台应该提供一种方法来为数千或数百万个虚拟线程维护不可变和可继承的每线程数据。” This is a comprehensible demand, but it seems that the authors are not sure and some more conceptual work is needed.这是一个可以理解的需求,但作者似乎并不确定,需要进行一些概念性的工作。

Now getting back to your question concerning the "allocation of thread locals on a carrier thread from the virtual thread".现在回到关于“从虚拟线程在载体线程上分配线程局部变量”的问题。 Why would you need that?你为什么需要那个? And caching objects in a thread-local makes sense in some scenarios (like caching the principal for the DB access in the example).在某些情况下,在线程本地缓存对象是有意义的(例如在示例中缓存 DB 访问的主体)。 Since virtual threads are relatively new (Java 19), it might be a bit early to declare thread-locals dead.由于虚拟线程相对较新(Java 19),因此宣布线程局部变量已死可能还为时过早。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM