Skip to content

Conversation

arturobernalg
Copy link
Member

Introduce VirtualThreadCloseableHttpClient to run classic transport on JDK 21 virtual threads while preserving CloseableHttpClient semantics and threading contract. Extend builder with useVirtualThreads(), virtualThreadNamePrefix(), virtualThreadExecutor(...), virtualThreadShutdownWait(...), and virtualThreadsRunHandler() to control execution. Add VirtualThreadSupport utility for JDK detection and per-task executor; include example and tests. Behavior remains unchanged unless enabled via the builder.

@arturobernalg arturobernalg requested a review from ok2c September 1, 2025 11:16
@arturobernalg arturobernalg changed the title TTPCLIENT-2394: Virtual threads support for classic client HTTPCLIENT-2394: Virtual threads support for classic client Sep 1, 2025
Add VirtualThreadCloseableHttpClient and builder options to run I/O (and optionally handler) on JDK 21 virtual threads.
Default behavior unchanged; classic path used when VT unavailable.
Include VirtualThreadSupport utility and example.
@arturobernalg arturobernalg force-pushed the HTTPCLIENT-2394-virtual-threads branch from 567f83d to 3c54bd2 Compare September 1, 2025 11:20
@rschmitt
Copy link
Contributor

I don't understand this at all. What problem does this solve, compared to simply calling the client from a virtual thread?

@arturobernalg
Copy link
Member Author

I don't understand this at all. What problem does this solve, compared to simply calling the client from a virtual thread?

Virtual threads let blocking HttpClient scale: thousands of concurrent requests without bloated platform threads.
They avoid thread-pool exhaustion/tuning and keep the classic API—no rewrite to async.
Best under latency/IO waits (DNS, TLS, slow servers): frees carriers, improves throughput and tail latency.
Cheaper context switches and straight-line stack traces vs. async callback/Future pyramids.

@rschmitt
Copy link
Contributor

Virtual threads let blocking HttpClient scale: thousands of concurrent requests without bloated platform threads.

In your example code, when you call:

            httpclient.execute(httpget, response -> { ... });

You are blocking a platform thread, which waits for the request to execute on a virtual thread. I don't see how you can realize increased concurrency in this way; it's still a thread-per-request model, and you might as well just call into the client directly from the OS thread.

The whole advantage of virtual threads over async/await is that virtual threads don't introduce function coloring. They are largely transparent to libraries. They allow everyone to write blocking, imperative code and scale up the concurrency by simply using a virtual thread factory instead of a conventional thread factory. And since HttpComponents is a pure Java library, we don't have to worry about pinning the carrier thread through blocking JNI calls or anything; Java already fixed that problem for us with JEP 353, and more recently JEP 491 made synchronized work correctly with virtual threads. I'm not sure there's a single thing we need to do to "enable" virtual threads; am I missing something here?

@arturobernalg
Copy link
Member Author

Virtual threads let blocking HttpClient scale: thousands of concurrent requests without bloated platform threads.

In your example code, when you call:

            httpclient.execute(httpget, response -> { ... });

You are blocking a platform thread, which waits for the request to execute on a virtual thread. I don't see how you can realize increased concurrency in this way; it's still a thread-per-request model, and you might as well just call into the client directly from the OS thread.

The whole advantage of virtual threads over async/await is that virtual threads don't introduce function coloring. They are largely transparent to libraries. They allow everyone to write blocking, imperative code and scale up the concurrency by simply using a virtual thread factory instead of a conventional thread factory. And since HttpComponents is a pure Java library, we don't have to worry about pinning the carrier thread through blocking JNI calls or anything; Java already fixed that problem for us with JEP 353, and more recently JEP 491 made synchronized work correctly with virtual threads. I'm not sure there's a single thing we need to do to "enable" virtual threads; am I missing something here?

It’s a small, optional convenience—not a scalability silver bullet.

Virtual threads let blocking HttpClient scale: thousands of concurrent requests without bloated platform threads.

In your example code, when you call:

            httpclient.execute(httpget, response -> { ... });

You are blocking a platform thread, which waits for the request to execute on a virtual thread. I don't see how you can realize increased concurrency in this way; it's still a thread-per-request model, and you might as well just call into the client directly from the OS thread.

The whole advantage of virtual threads over async/await is that virtual threads don't introduce function coloring. They are largely transparent to libraries. They allow everyone to write blocking, imperative code and scale up the concurrency by simply using a virtual thread factory instead of a conventional thread factory. And since HttpComponents is a pure Java library, we don't have to worry about pinning the carrier thread through blocking JNI calls or anything; Java already fixed that problem for us with JEP 353, and more recently JEP 491 made synchronized work correctly with virtual threads. I'm not sure there's a single thing we need to do to "enable" virtual threads; am I missing something here?

It’s a small, optional convenience—not a scalability silver bullet.
It standardizes VT wiring (naming, shutdown), preserves classic blocking/error semantics (e.g., map executor rejection to IOException), and can also run the response handler on the same VT when requested.
Default behavior is unchanged; if you already launch calls on VTs, ignore it.
Net result: less boilerplate and consistent, documented VT behavior without coloring the API.

@ok2c
Copy link
Member

ok2c commented Oct 11, 2025

@arturobernalg Likewise, I do not really understand the benefits of this change especially for the classic transport where the users are expected to manage the threads anyway.

I am not in favor of this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants