According to a [request of the OP](https://stackoverflow.com/questions/78554636/why-java-virtual-threads-use-forkjoinpool#comment138531825_78554636) to configure `VirtualThread` with a custom thread pool, the following solution is discussed below.

As of JDK 21 there is no way to set up a custom `Executor`/`scheduler` for `VirtualThread` class: this class along with `ThreadBuilders.VirtualThreadBuilder` and `VirtualThreadFactory` are `java.lang` package-private and the only way to do so is to use Reflection.

 	public static Thread.Builder customVirtualThreadPool(Executor executor) throws ReflectiveOperationException {
		var ctor = Class.forName("java.lang.ThreadBuilders$VirtualThreadBuilder")
				.getDeclaredConstructor(Executor.class);
		ctor.setAccessible(true);
		return (Thread.Builder) ctor.newInstance(executor);
	}

As access to `java.lang` classes is attempted it might be necessary to explicitly allow it in application parameters 

 --add-opens=java.base/java.lang=ALL-UNNAMED

as [Module java.base does not "opens java.lang"](https://stackoverflow.com/questions/74006627/module-java-base-does-not-opens-java-lang-java-17-0-4-1) thread suggests. 

Note that this can be used to set up custom `ForkJoinPool` instance as an actual pool in order to get an access to work-stealing count.

For Spring Boot/Tomcat application further configuration does not differ from configuration of any other such `Executor`.

As usual, to run Tomcat worker threads on this `Excecutor`, the ones that executes `@Controller`s methods, annotated with `@RequestMapping` and analogous, customize Tomcat Protocol Handler:

 @Bean
 public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
 return protocolHandler -> {
 try {
				protocolHandler.setExecutor(
						customVirtualThreadPool(Executors.newCachedThreadPool())::start);
			} catch (ReflectiveOperationException e) {
				throw new BeanInitializationException("Cannot create custom Virtual Thread Executor", e);
			}
 };
 }

Similarly, to run Spring-controlled asynchronous methods on this `Executor`, configuration will look like this: 

 @Configuration
 @EnableAsync
 public class AppConfig {

	 @Bean
 	public TaskExecutor taskExecutor() {
		 try {
			 return new TaskExecutorAdapter(customVirtualThreadPool(Executors.newCachedThreadPool())::start);
		 } catch (ReflectiveOperationException e) {
			 throw new BeanInitializationException("Cannot create custom Virtual Thread Executor", e);
		 }
	 }

 }

and the asynchronous method like this:

 @Service
 @Async // will use `taskExecutor` by default, if there are more than one in the app, then Bean name can be specified
 public class AsyncService {

	 public void doSomething() {
		 ...
	 }

 }

Note that presence of `spring.threads.virtual.enabled = true` in application property file is not required in both cases.

The above is applicable to Spring Boot 3.2.0; be cautious with older Tomcats, they may or may not support the configuration above.