Performance Issues with Virtual Threads + ThreadLocal Caching in Third-Party Libraries (JDK 25)

Stig Rohde Døssing stigdoessing at gmail.com
Sat Jan 24 16:11:01 UTC 2026


Hi Jianbin,

Sorry to butt in, but I think the question you are asking is a little odd.
You have a library that uses ThreadLocals for reusing expensive resources
(buffers in this case). The way to make such a library work well with
virtual threads is to redesign the library to avoid using TLs in this
manner. For example, you could make the library keep a pool of these
resources for reuse in a non-TL structure, like concurrent
maps/lists/queues.

But once you set the limitation that the library can't be adjusted, you are
forced into awkward workarounds. This is because the main advantage of
virtual threads is to allow you to write code in a thread-per-task style,
but the presence of these TLs makes threads precious resources that must be
reused across tasks, which loses you the ability to use virtual threads in
this way.

If you are unable to adjust the library and really want to use virtual
threads for part of your code, an option is to isolate the TL-using code so
it runs on a platform thread pool. You would then write most of your code
in thread-per-task style with virtual threads, but make the virtual threads
hand off work that needs the TLs to the thread pool, blocking the virtual
thread until that work completes.

If that is not an option, and you don't want that kind of handoff, you are
forced to create a pool of threads, as you found. But at that point, I
don't really understand why you want to use virtual threads at all. Once
you are making a pool of 200 threads you reuse, it doesn't really matter if
those threads are virtual or platform threads. You are forced to abandon
the thread-per-task style either way.

I don't think there is a great solution that will let you use
thread-per-task style virtual threads with a library that uses TLs for
resource reuse. The best you are likely to be able to do is various
workarounds, with various drawbacks. It might be better to aim for
reworking the library, and sticking with platform threads until you can do
that?

Den lør. 24. jan. 2026 kl. 14.14 skrev Jianbin Chen <jianbin at apache.org>:

> Hi Alan,
>
> I ran my example on JDK 21 because it uses Thread.sleep. In an earlier
> message on the mailing list I learned that virtual‑thread performance on
> JDK 25 was worse for this kind of scenario compared with JDK 21, and that
> the issue is supposed to be fixed in JDK 25.0.3 — which has not been
> released yet.
>
> That said, this does not affect the main point of my message: I’m asking
> for advice about using pooled virtual threads to work around third‑party
> libraries that implement buffer pools via ThreadLocal.
>
> Thank you,
> Jianbin Chen
>
> Alan Bateman <alan.bateman at oracle.com> 于2026年1月24日周六 16:34写道:
>
>>
>>
>> On 24/01/2026 05:55, Jianbin Chen wrote:
>> > :
>> >
>> > I constructed the Executor directly with
>> > Executors.newVirtualThreadPerTaskExecutor();
>> > however, the run results still show that the pooled virtual‑thread
>> > behavior outperforms the non‑pooled virtual threads.
>>
>> This looks like it is benchmarking Thread.sleep so a different topic to
>> that of libraries that are caching objects in thread locals.
>>
>> For the Thread.sleep test then it would easier to discuss if converted
>> to a JMH benchmark as there are warmup issues in the test you included.
>> Also just to note that the Thread.sleep implementation has changed
>> significantly changed since JDK 21 so you will see very different
>> results with JDK 25 runs (some of the messages in the discussion speak
>> of JDK 21, the subject line in the mails say "JDK 25", so I'm guessing
>> you might be testing both).
>>
>> -Alan
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20260124/86814505/attachment-0001.htm>


More information about the loom-dev mailing list