3

I am currently building a java-servlet-based web application that should offer its service to quite a lot of users (don't ask me how much "a lot" is :-) - I don't know yet).

However, while the application is being used, there might occur some long-taking processing on the serverside. In order to avoid bad UI responsiveness, I decided to move these processing operations into their own threads. This means that once a user is logged in, it can happen that 1-10 threads run in the background (per user!).

I once heard that using multiple threads in a web application is a "bad idea".

Is this true and if yes: Why?

Update: I forgot to mention that my application heavily relies on ajax calls. Every user action causes a new ajax call. So, when the main servlet thread is busy, the ajax call takes very long to process. That's why I want to use multiple threads.

Timo Ernst
  • 15,243
  • 23
  • 104
  • 165
  • Why would the UI "freeze"? It sounds like you're thinking in native terms... in a web app you should either be using AJAX or you'll get a slow page load, not really a "freeze". – Jon Skeet Jul 01 '11 at 13:04
  • I forgot to mention that my application heavily relies on ajax calls. Every user action causes a new ajax call. So, when the main servlet thread is busy, the ajax call takes very long to process. – Timo Ernst Jul 01 '11 at 13:07
  • Sure, but that's not the same as a "UI freeze"... – Jon Skeet Jul 01 '11 at 13:13
  • @Jon Skeet You're right. Sorry for the misunderstanding. – Timo Ernst Jul 01 '11 at 13:15
  • "when the main servlet thread is busy" what makes you think there is only one? Servlet containers (typically) handle each request in a separate thread from a pool, there is not one thread for all requests. – matt b Jul 01 '11 at 14:29
  • @matt b Mhh.. you're right! I am starting to wonder why my long running task is blocking the UI then. – Timo Ernst Jul 01 '11 at 14:32
  • A stateful bean is unique and linked to one user. And I guess AJAX uses stateful beans. So it could slow down your webpage response time. – toto2 Jul 01 '11 at 15:22

5 Answers5

5

It is a bad idea to manually create the threads yourself. This has been discussed a lot here in SO. See this question for example.

Another question discusses alternative solutions.

Community
  • 1
  • 1
kgiannakakis
  • 103,016
  • 27
  • 158
  • 194
  • This seems a little too simplistic for me. The bad idea isn't just having non-request-processing threads, it's creating them in a way where they won't respond to the server shutting down or other lifecycle events, or spawning too many without a maximum ceiling, etc. The alternative solution linked to involves using Quartz which... spawns threads in the background. The only difference is using a well-regarded library versus writing it yourself. – matt b Jul 01 '11 at 14:28
  • You are right that the bad idea refers to the manual construction of threads. I have edited my answer. – kgiannakakis Jul 01 '11 at 14:33
  • @matt b @kgiannakakis I noticed that most alternate solutions require me to set a max. number of threads for the application. What would be a good way to determine a reasonable value for the max. number of threads that I need? I am running a multi-user web application and I expect most users to spawn 1-10 threads (maybe more). I don't know how many users I will end up though. – Timo Ernst Jul 02 '11 at 10:51
  • Test and measurement of average number of users, what the drag on system resources is, etc. – matt b Jul 02 '11 at 12:52
  • It is worth noting that almost everyone I run into renders this advice under the mistaken assumption that it means Runnables=Threads and that using Executors is equivalent to doing "new Thread()". I could be mistaken but my understanding is that Runnables and Executors are there precisely so we don't have to do "new Thread()" and it is "new Thread()" that we should avoid doing. Runnable != Thread – Bane Sep 06 '12 at 15:23
2

The "bad idea" isn't multiple threads. Java EE was originally written so multi-threading was in the hands of the app server, so users were discouraged from starting their own threads.

I think what you really want is asynchronous processing for long-running tasks so users won't have to wait for them to finish before proceeding.

You could do that with JMS and stay within the lines in the Java EE coloring book. I think that it's safer to do on your own, now that there are new classes and constructs in the java.util.concurrent package.

It's still not an easy thing to do. Multi-threaded code isn't trivial. But I think it's easier than it used to be in Java.

Part of the problem might be that you're asking that servlet to do too much. Servlets should listen for HTTP request and orchestrate getting a response from other classes, not do all the processing themselves. Perhaps your servlet is telling you that it's time to refactor a bit. This will help your testing, since you'll be able to unit test those asynch classes without having a servlet/JSP engine running.

AJAX calls to services via HTTP need not block. If the service can return a token, a la FedEx, that tells the app when and how to get the response, there's no reason why the service can't process asynchronously. It's an implementation detail for the services that you should hide from clients.

duffymo
  • 305,152
  • 44
  • 369
  • 561
1

1.
Brilliant Idea.
It's not common, but it's nothing wrong.
If you think asynchronous tasks are needed for better user experiences. Just use it.

2.
You need to be careful with it.
2.1.
Creating and destroying threads add a lot of overhead to your server.
You'd better use a executor, like java.util.concurrent.ThreadPoolExecutor.

2.2.
Don't just use Executors.newFixedThreadPool(). It is for beginners and hides dangerous details.
You need to know the edge behavior of ThreadPoolExecutor, and configure it properly.

  • How much threads are enough for your task? You need to calculate it out.
  • What would happen if there is no free theads in your pool? Different configurations can make it wait, cache, or abandon new tasks. What should you expect?
  • What would happen if a task runs for too long(such as an infinite loop)? There is no real timeout-and-exit mechanism in java. How do you prevent these.
bellscape
  • 43
  • 6
0

If the application requires it, then I say go ahead and do the background threads, but, since you don't know how many users you will have, you are taking a great risk that you will overwhelm your server. You might consider some alternatives, if they will work in your situation. Can you run the background tasks completely offline, e.g. in a batch job? Can you limit the number of threads that each logged in user will need? How will you get the results of the background threads back to the user?

Mitch
  • 989
  • 1
  • 9
  • 25
0

This is a bad idea for three main reasons:

  1. Excessive number of running threads can kill system resources and cause some strange things such as starvation and priority inversion. Often this can be solved with a thread pool.
  2. User session duration is unpredictable. The user can fire an action and go for a coffee, or he/she might complain for the delay an redo the action. This can cause creation of multiple background jobs, so requires complex control, and when we talk about threads, we never know for sure if we didn't left race conditions or unantecipated scenarios.
  3. Most likely servlets will have some interaction with the threads. Now suppose your application needs to be scaled, so you use a clustered container (after all, you have "a lot" of users). The container can passivate a session and restore it in another node. But your threads will remain in the initial node, so the link between session and threads will be broken. This ends in unexpected exceptions and error 500 - server failure.

I think the best solution is to design your application so that it won't create so many background threads.

But if you insist or really need it, try using Java EE message driven beans (MDBs) and make your servlet invoke it using JMS, like @duffymo said.

The challenge is how to make communication between MDBs and user sessions. Perhaps your servlet can create a JMS queue or topic and send it to MDBs for them to reply, but I don't know if the servlet side of JMS connection can be passivated and restored.

Another forms of communication would be JNDI or an external database or file, but this requires polling, which might be unresponsive or CPU-excessive.

fernacolo
  • 7,012
  • 5
  • 40
  • 61
  • The servlet won't create a queue or topic; that's configured in the app server before the app starts up. The servlet (or, better yet, a service) will instantiate a client that sends a message to the queue or topic. – duffymo Jul 01 '11 at 15:00
  • @duffymo About the client, of course. Actually, the servlet will be a client. Now for the queue, JMS allows creation of temporary queues, as you can see [here](http://stackoverflow.com/questions/1587231/how-to-create-a-temporary-jms-queue-and-connect-to-it-by-name). – fernacolo Jul 01 '11 at 18:08