We're using NIO2 with a StandardThreadExecutor we declared in our server.xml, and noticed in our logs that REST controllers run in a default thread pool, i.e. thread names in the format Thread-x are logged, e.g. 2024-02-29 14:42:48,313 (Thread-6:[]) INFO ... Upon further debugging we noticed that the threadGroup field of the Nio2Endpoint is NULL. Looking at the source code, it seems that this condition in the bind() method evaluates to false: if (getExecutor() instanceof ExecutorService) { threadGroup = AsynchronousChannelGroup.withThreadPool((ExecutorService) getExecutor()); } which makes sense, since StandardThreadExecutor doesn't implement ExecutorService. We suspect that the NULL threadGroup causes NIO2 to use the default thread pool. If we specify the thread attributes on the connector itself instead of passing an executor attribute, the correct channel group/thread pool is used: 2024-02-29 15:15:07,166 (http-nio2-1776-exec-7:[]) INFO ... since in that case, the endpoint instantiates a standard ThreadPoolExecutor which implements ExecutorService. if (getUseVirtualThreads()) { executor = new VirtualThreadExecutor(getName() + "-virt-"); } else { TaskQueue taskqueue = new TaskQueue(); TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority()); executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf); taskqueue.setParent( (ThreadPoolExecutor) executor); }
I think it is possible to upgrade the implementations to ExecutorService and I will do that. However, NIO2 needs its own exclusive pool so it is probably not very useful to configure a custom one like this.
Hi Remy, Thank you for the quick reply! Just to be clear, we are only passing this executor to a single connector, so it is used exclusively. I think that the current behaviour is a bit inconsistent in the sense that if one implemented a custom Catalina executor, e.g. a StandardThreadExecutor subclass which also implements ExecutorService, the executor and its thread pool would be used. This instanceof check is only visible in the source code, and it's not clear to consumers of the NIO2 connector.
The fix will be in 11.0.0-M18, 10.1.20 and 9.0.87.