Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring ThreadPoolTaskExecutor in a Tomcat webapp - bad practice?

Recently in my project I had a need to perform some tasks asynchronously. Since we are running a webapp with Spring inside a Tomcat, the ThreadPoolTaskExecutor provided by Spring was a solution.

However the architect raised some objections, stating that it is horrible/forbidden/absolute evil to spawn thread/have a thread pool in a webapp.

By searching a little bit on the net and on StackOverflow, I realized that yes, it is a bad practice to have your own thread pool inside a Java EE container. The rationale was that if you have your own thread pool, the container is not aware of it and cannot manage resources properly. It is especially critical when you need to do some hot deployments of your webapp.

Now, our use case is a Spring webapp run inside Tomcat. First, can we consider the Spring container like a light Java EE container ? In this case it is Spring which manages directly the threadpool lifecycle and not the application itself, isn't it ?

Second, does the hot deployment argument also apply for this configuration ?

And yes, I know that it is possible to declare a worker pool directly in Tomcat and inject it through JNDI into Spring. But it's a little bit hassle compared to the direct ThreadPoolTaskExecutor facility provided by Spring

So my final question is: is the objection of the architect relevant in my case ?

Thanks for your suggestions and thoughts on this topic.

like image 863
doanduyhai Avatar asked Aug 08 '12 20:08

doanduyhai


2 Answers

There are various levels of evil, and not all of them actually count as evil in every situation.

Creating threads on demand instead of using a pool is typically seen as evil indeed, but that's not just true for Java EE and holds for almost any kind of server application.

In Java EE, creating your own threads is especially not allowed in the EJB container. This is among others because Java EE containers might invisibly store contextual data in thread local storage, which is lost if code starts executing in its own thread.

The web container however has no such restrictions and according to the spec it's more or less legal to have thread pools. This is for instance the reason why people used to start up Quartz from a web module in an EAR, even when only EJB modules were used, or why code from within the web module could register callback listeners to unmanaged JMS queues, but EJBs could not do this.

However, in practice creating threads (via pools) actually does almost always work, as long as you keep in mind that if you use e.g. EJB you need to acquire instances from JNDI in code running in those threads and don't pass references of EJBs to those unmanaged threads. Of course you do need to take care to shut-down your pool, but nearly every kind of startup listener in Java EE has a corresponding shut-down listener where you can do this.

Java EE does have some official ways which mitigate the need for creating your own pools:

  • The @Asynchronous annotation
  • Sending a JMS message and handling it in an MDB
  • Using the AsyncContext#start() (which is of limited general usage, see What's the purpose of AsyncContext.start(...) in Servlet 3.0?)
  • Using the work manager API (this is actually intended to be used by vendors of JCA connectors and thus not streamlined for end users, see http://www.adam-bien.com/roller/abien/entry/easy_threading_and_concurrency_in and http://connectorz.adam-bien.com)

Yet, some algorithms require separate thread pools in order to prevent dead-lock possibilities. Since none of the Java EE solutions give you the absolute guarantee that the work is done by different thread pools, there is sometimes simply no other plausible way than create your own pool.

So in the last situation, it's actually more evil to open up your code to dead-locks than it is to create your own thread-pool.

like image 145
Arjan Tijms Avatar answered Oct 22 '22 21:10

Arjan Tijms


Imho having self managed TaskExecutors inside a Java EE application is not bad practice, if its properly isolated. Separating asynchronous tasks into an isolated instance introduces a new level of complexity, a new dependency and reduces performance.

The architects argument that is not managed is of course nullable, since the container isnt aware/ owner of many instances (e.g. static references) and, you can configure the executor itself within a @Configuration class, or inside the spring config file, making at least the executor itself managed by the container.

Additionally, Spring itself exposes several methods of executing methods scheduled, for exampel by using the @Scheduled annotation (http://static.springsource.org/spring/docs/3.0.x/reference/scheduling.html)

A hot deployment dependency depends on how your work queue is configured, and the way your asynchronous task handles them.

like image 31
Marius Avatar answered Oct 22 '22 20:10

Marius