18.10. Thread Management

18.10.1. Server-Side Thread Management

Each HornetQ Server maintains a single thread pool for general use, and scheduled thread pool for scheduled use. A Java scheduled thread pool cannot be configured to use a standard thread pool, otherwise we could use a single thread pool for both scheduled and non scheduled activity.
When using old (blocking) IO, a separate thread pool is used to service connections. Since old IO requires a thread per connection, it is not recommended to get the threads from the standard pool as the pool will get exhausted if too many connections are made. This results in the server hanging, since it has no remaining threads to do anything else. If you require the server to handle many concurrent connections you must use NIO, not old IO.
When using new IO (NIO), by default, HornetQ uses a number of threads equal to three times the number of cores (or hyper-threads) as reported by .getRuntime().availableProcessors()Runtime for processing incoming packets. To override this value, you can set the number of threads by specifying the nio-remoting-threads parameter in the transport configuration.

18.10.1.1. Server Scheduled Thread Pool

The server scheduled thread pool is used for most activities on the server side that require running periodically or with delays. It maps internally to a java.util.concurrent.ScheduledThreadPoolExecutor instance.
The maximum number of thread used by this pool is configured in standalone.xml or domain.xml using the scheduled-thread-pool-max-size parameter. The default value is 5 threads. A small number of threads is usually sufficient for this pool.

18.10.1.2. General Purpose Server Thread Pool

The general purpose thread pool is used for most asynchronous actions on the server side. It maps internally to a java.util.concurrent.ThreadPoolExecutor instance.
The maximum number of thread used by this pool is configured in standalone.xml or domain.xml using the thread-pool-max-size parameter.
If a value of -1 is used, the thread pool has no upper bound and new threads are created on demand if there are not enough threads available to fulfill a request. If activity later subsides then threads are timed-out and closed.
If a value of n, where n is a positive integer greater than zero, is used then the thread pool is bounded. If more requests come in and there are no free threads are available in the pool and the pool is full then requests will block until a thread becomes available. It is recommended that a bounded thread pool is used with caution since it can lead to dead-lock situations if the upper bound is chosen to be too low.
The default value for thread-pool-max-size is 30.

18.10.1.3. Expiry Reaper Thread

A single thread is also used on the server side to scan for expired messages in queues. We cannot use either of the thread pools for this since this thread needs to run at its own configurable priority.

18.10.1.4. Asynchronous IO

Asynchronous IO has a thread pool for receiving and dispatching events out of the native layer. It is on a thread dump with the prefix HornetQ-AIO-poller-pool. HornetQ uses one thread per opened file on the journal (there is usually one).
There is also a single thread used to invoke writes on libaio. It is done to avoid context switching on libaio that would cause performance issues. This thread is found on a thread dump with the prefix HornetQ-AIO-writer-pool.

18.10.2. Client-Side Thread Management

On the client side, HornetQ maintains a single static scheduled thread pool and a single static general thread pool for use by all clients using the same classloader in that JVM instance.
The static scheduled thread pool has a maximum size of 5 threads, and the general purpose thread pool has an unbounded maximum size.
If required HornetQ can also be configured so that each ClientSessionFactory instance does not use these static pools but instead maintains its own scheduled and general purpose pool. Any sessions created from that ClientSessionFactory will use those pools instead.
To configure a ClientSessionFactory instance to use its own pools, use the appropriate setter methods immediately after creation. For example:
ServerLocator locator = HornetQClient.createServerLocatorWithoutHA(...)
ClientSessionFactory myFactory = locator.createClientSessionFactory();
myFactory.setUseGlobalPools(false);
myFactory.setScheduledThreadPoolMaxSize(10);
myFactory.setThreadPoolMaxSize(-1);
If you are using the JMS API, you can set the same parameters on the ClientSessionFactory and use it to create the ConnectionFactory instance. For example:
ConnectionFactory myConnectionFactory = HornetQJMSClient.createConnectionFactory(myFactory);
If you are using JNDI to instantiate HornetQConnectionFactory instances, you can also set these parameters in the standalone.xml or domain.xml file where you describe your connection factory. For example:
<connection-factory name="ConnectionFactory">
   <connectors>
      <connector-ref connector-name="netty"/>
   </connectors>
   <entries>
      <entry name="ConnectionFactory"/>
      <entry name="XAConnectionFactory"/>
   </entries>
   <use-global-pools>false</use-global-pools>
   <scheduled-thread-pool-max-size>10</scheduled-thread-pool-max-size>
   <thread-pool-max-size>-1</thread-pool-max-size>
</connection-factory>