Alternative of ThreadLocal for virtual threads
Asked Answered
U

2

5

I am a new to the latest Java versions and I found this concept of virtual threads which many servers are now using.

In the same context I wanted to use a ThreadLocal field in the virtual thread concept. Not sure how to implement it.

Upholstery answered 19/8 at 17:16 Comment(3)
Just a personal opinion: You should not use ThreadLocal in new code. ThreadLocal is useful when you're porting old, single-threaded code that uses static variables into a multi-threaded environment. If you're not declaring static variables, then there's probably no reason to use ThreadLocal, and you should not be declaring static variables in new code. Static and ThreadLocal can make your code significantly more difficult to maintain and significantly more difficult to test.Wincer
FWIW, ScopedValue avoids most of the problems with ThreadLocal.Hawes
What exactly is your question? What problem do you have with using ThreadLocal with virtual threads that you don't experience with platform threads?Zebada
H
6

In general, the successor API for ThreadLocal for virtual threads is not yet released. You're looking for ScopedValue, which is still in preview. See JEP 481: Scoped Values for Java 23.

With that said, ThreadLocal will still work with virtual threads. Almost all its use cases are better served by other approaches, however.

Hawes answered 19/8 at 17:40 Comment(9)
How does ThreadLocal not work well with virtual threads? I see no fault described in the JEP. To quote: “Virtual threads support thread-local variables (ThreadLocal) and inheritable thread-local variables (InheritableThreadLocal), just like platform threads, so they can run existing code that uses thread locals.” The only caveat there is a caution about quantity.Goldfinch
@BasilBourque IMHO, the intent to be able to use virtual threads is to be able to spawn a thread per request, which shall prepare applications for a higher number of threads and that's where one is supposed to be careful in consideration of thread locals.Aguedaaguero
@Aguedaaguero Your Comment does not explain “not perform especially well”. Your Comment restates the caution over quantity that I mentioned from the JEP. But that is a far cry from not working well. The Project Loom team went to extra efforts to make sure that ThreadLocal works exactly the same in virtual threads as they do in platform threads. Implying otherwise will prompt unfounded rumors and misunderstanding amongst developers.Goldfinch
@BasilBourque, yes, I'd rather expect Loom team to remove any cautions against ThreadLocal. After all, ThreadLocal is nothing more than a Map from Thread to Object. Interesting to learn, is it possible to "intercept" the reference to carrier's Map and substitute virtual thread's own Map?Chimene
In general, ThreadLocals have a few use cases. Caching, pooling, and similar approaches are a widespread historical use of ThreadLocal and do indeed perform notably worse than, for example, not pooling at all. Other use cases, such as the MDC case, were always intended to pass implicit state along through a chain of execution, and ScopedValue is significantly preferred for that. In general, the Loom developers don't think there are really remaining good uses for ThreadLocal, based on my impressions from presentations and discussions with them at the JVM Language Summit.Hawes
@igor.zh, while that's a good way to think of the API, that's not the actual implementation; it's more like each thread has-a Map from ThreadLocals to values. (Importantly, that prevents memory leaks.)Hawes
@LouisWasserman, sure, my global map was just a very abstract view. In respect of your comment, which discusses a replacement of ThreadLocal with ScopedValue, do you really think it is possible for frameworks like Spring Security? I have entire answer that doubts it is conceptually possible; might miss something, though.Chimene
I've never used Spring or any of its associated frameworks. I certainly expect to rewrite our own frameworks to fully avoid ThreadLocal.Hawes
I see... Completely different world and way of thinking. Let's see how Spring will react on Structured Concurrency :) If they will the task will be more complex than an introduction of spring.threads.virtual.enabled.Chimene
C
4

Yes, indeed ScopedValue is a recommended replacement for ThreadLocal. JavaDoc for ScopedValue says:

A ScopedValue should be preferred over a ThreadLocal for cases where the goal is "one-way transmission" of data without using method parameters.

Oracle doc for Virtual thread, section Don't Cache Expensive Reusable Objects in Thread-Local Variables goes along the similar lines.

This is understandable. When virtual thread dismounts from one carrier thread and mounts on another, the thread context must be transferred and ThreadLocal is evidently a part of this context.

But that's a theory. The reality is different and might depend on the stack you use. The problem with ScopedValue actually lies ... in its advertised advantage. The same JavaDoc, mentioned above, reads:

A ThreadLocal has an unbounded lifetime and thus continues to have a value after a method completes, unless explicitly removed.

The truth is that very often it is exactly what a framework or library needs: not that much of "unbounded lifetime", but rather a possibility to jump into a thread context at any point of execution/stack and plant there some status. As a result, ThreadLocals are used by innumerable amount of frameworks or libraries: Spring Security, Spring Web, MDC, Spring Data JPA, Micrometer, Spring Transaction Management - the list goes on. The usage of ThreadLocals is so ubiquitous that it is difficult to name a system which do not use ThreadLocal.

So, unbeknownst to you, your system might use bunch of ThreadLocals.

In some cases it might be marginally possible to replace ThreadLocal with ScopedValue: Spring Security Virtual Threads and ThreadLocal thread discusses such attempt in respect of Spring Security, and Logback: availability of MDCs in forks created inside a StructuredTaskScope thread - in respect of MDC, but it is easy to see how awkward and flawed such design might be and the main reason is that ScopedValue has to be bound to a thread conceptually at the time of its spawning.

Granted, it is one of main goals of Structured Concurrency, but it also means that ScopedValue is not a drop-in replacement for ThreadLocal, at least for now.

How the situation might change in near future? Will frameworks and libraries authors jump into Structured Concurrency? I'd rather take the advise Don't Cache Expensive Reusable Objects in Thread-Local Variables lightly and won't attempt to use Structured Concurrency in the cases it does not fit to.

Chimene answered 20/8 at 3:16 Comment(16)
As for me, the JEP discussed looks broken: since it assumes the caller may override scoped values, the JEP must provide the ability to retrieve values been overridden, i.e. instead of single ScopedValue.get() method I would expect to have at least something like ScopedValue.getLast() and List<T> ScopedValue.getChain()Polarimeter
Interesting turn! In what respect ScopedValue is then different from any other value holder, like Integer and other wrappers or Atomic*? The idea of ScopedValue, is, as far as I can see, not to have history of changes, but to have it operating inside of StructuredTaskScope. Which is more secure than, for example, to have ThreadLocal being set and reset in servlet Filter.Chimene
passing mutable structures via ScopedValue turns last into ThreadLocal from "security" perspective.Polarimeter
So, due to mutability of ScopedValue you deny any value of it in the light of Structured Concurrency?Chimene
nope, you did say that. My opinion is following: poor API causes developers to write either tricky or buggy code (what are the actual consequences of passing mutable structures via ScopedValue?). JEP 481 do propose a poor API, that is embarrassing.Polarimeter
@AndreyB.Panfilov do you have a suggestion, how an API that can prevent the use of mutable objects (in Java) may look like? Besides the impossible of this demand, it’s interesting how your rants evolved, the first criticism has no relationship to the last at all. It looks like your opinion about the API was set first, then you tried hard to find reasons for it.Wyler
@Holger, API... SPI... and everybody squarely ignores the problem of migration to ScopedValue for the systems, which heavily rely on ThreadLocals. If you look at the virtual thread/project Loom-related questions, good half of them are Spring-, Spring Boot-, Tomcat-, MDC-, Micrometer-related, and even if a question is purely generic or theoretical, there is a good chance the OP of such question still uses some of those. And those systems have a tremendous problem with migration from ThreadLocal to ScopedValue.Chimene
@Wyler I can't recall a situation when I was need to introduce ThreadLocal without interesting in keeping information about previous values. Basically, logging capabilities do require such feature - we need to log a chain of security subjects, a chain of business operations etc. Yes, API is not able to prevent developer from doing stupid things, however, at current moment it enforces developer to do that.Polarimeter
@AndreyB.Panfilov, that sounds extremely strange. Imagine a pool of threads, e.g. Tomcat working threads, Spring Security Filter injects new Authentication object into ThreadLocal of a thread that handles a request, and when the handling of the request completes the Filter removes this object from this ThreadLocal. Why this Spring Security Filter would be interested in this thread's ThreadLocal's history?Chimene
@Chimene Well, Subject A hits REST API, which in turn hits internal privileged method which impersonalizes Subject A and current security subject becomes Subject SYS, after that, inside logging framework, I need to log/audit both Subject SYS and Subject A, since the last one have initiated the changes. Then, my internal privileged method wants to send data somewhere outside and obviously it needs to use Subject A (i.e. the first security subject in chain). Please check how @Secured("RUN_AS_...") works in spring.Polarimeter
@AndreyB.Panfilov I don’t see how your requirement relates to your previous rants about ScopedValue being “mutable” (despite it’s immutable but just can’t prevent the referenced object from being mutable, as no class in Java can). You want a context object knowing its history? It’s as easy as record Whatever(Whatever previous, OtherType currentValue) {} Store that in a ScopedValue and you’re done. Besides that, apparently the current frameworks learned to live with ThreadLocal not having a history, so why should it suddenly be a problem with ScopeValue?Wyler
@Wyler and how am I supposed to traverse such structures? Do all frameworks need to change their API? That is ridiculous, I did provide two obvious cases when the historic values of context are required, there are another dozens of cases when it is not so obvious, the JEP does not even consider such cases (just because it is fundamentally broken).Polarimeter
@AndreyB.Panfilov of course, they don’t have to change anything. ThreadLocal has no history and ScopeLocal has no history. Nothing has changed in this regard in the last twenty years. This was the status quo even before those frameworks were created. And no, a feature is not broken just because one guy on the internet says so.Wyler
That actually sounds like ScopeLocal is not going to improve anything, oh no...: someone won't need to write try/finally anymore - best twenty years improvement ever.Polarimeter
@AndreyB.Panfilov, just was going to say what @Wyler said. You may build your own system with historical and other features , but both ThreadLocal and ScopedValue are about binding a value to a thread only. If you want, for example, advanced push/pop discipline, you could easily build it a top of primitive, but basic set/get. Current ThreadLocal and ScopedValue design just follows separation of concepts principle.Chimene
As for 20 years of improvements, here discussion becomes philosophical... Many, me including, think functional programming in Java is an introduction of huge mess, which is hard to debug, difficult to read and breaks Java concepts, var is another example, but many think opposite. Virtual threads is an obvious improvement, but if you look how its implementation breaks OOP principles...Chimene

© 2022 - 2024 — McMap. All rights reserved.