9

Short question: Within a Tomcat 6 app - how can I run a (separate) thread-pool?

What is the best solution for running a thread pool?

Long question:
I have a simple need here;
Poll a database for some data, while allowing web clients wait for an answer (long poll connections).
When this data is available at the database I'll send a reply to the relevant client.

Saying so, I prefer not to dive into any framework at the moment (quartz scheduler maybe?).

Therefore, as I conclude, I'll need a thread pool to do the job in the background.

So if Thread is what I'm about to use (actually Runnable), which class can organizes it all? Is there sort of a ThreadPool solution? Any recommendation?

Poni
  • 11,061
  • 25
  • 80
  • 121
  • So, you want to have a long-running request that sits and waits for a background task to poll the database? Why not just have your clients (in)directly poll the database? That will significantly simplify your software. – Christopher Schultz Jun 15 '12 at 16:00
  • @ChristopherSchultz, having the Tomcat's http request handler workers do long loops or the sort is a terrible way to exhaust the service. Imagine Tomcat has for say 5 serving threads - if I get those to be stuck with database polls then other clients won't be able to talk with the system. Another thread pool for that purpose is a must, even if it means complicating the code (which is not THAT much by the way). – Poni Jun 15 '12 at 19:47

3 Answers3

17

Answering your short question:

In JVM thread pools are abstracted behind java.util.concurrent.ExecutorService interface. There are different implementations of this interface but in most cases methods of this interface would suffice.

To create specific thread pool, take a look at java.util.concurrent.Executors class: http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/Executors.html which contains static factory methods for creating different implementations of ExecutorService interface. You may be interested in newFixedThreadPool(int threadsNumber) and newCachedThreadPool methods.

For more general information on Executors in JVM you may want to read following Oracle's tutorial: http://docs.oracle.com/javase/tutorial/essential/concurrency/executors.html

So, to use thread pool (ExecutorService) under Tomcat you should do the following:

.1. Create and register in web.xml instance of javax.servlet.ServletContextListener interface (which would act like an entry point to your webapplication) if it's not done yet.

.2. In contextInitialized(ServletContextEvent) method, you create instance of ExecutorService (thread pool) and store it in ServletContext attribute map, so that it can be accessed from any point in you webapp e.g.:

// following method is invoked one time, when you web application starts (is deployed)
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
    // ...
    final int numberOfThreads = ...;
    final ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads); // starts thread pool
    final ServletContext servletContext = servletContextEvent.getServletContext();
    servletContext.setAttribute("threadPoolAlias", threadPool);
    // ...
}

// following method is invoked one time when your web application stops (is undeployed)
public void contextDestroyed(ServletContextEvent servletContextEvent) {
    // following code is just to free resources occupied by thread pool when web application is undeployed
    final ExecutorService threadPool = (ExecutorService) servletContextEvent.getServletContext().getAttribute("threadPoolAlias");
    threadPool.shutdown();
}

.3. Somewhere in Servlet.service method or anywhere in your webapp (you should be able to obtain reference to ServletContext almost anywhere from webapp):

Callable<ResultOfMyTask> callable = new Callable<ResultOfMyTask>() {
    public ResultOfMyTask call() {
        // here goes your task code which is to be invoked by thread pool 
    }
};

final ServletContext servletContext = ...;
final ExecutorService threadPool = (ExecutorService) servletContext.getAttribute("threadPoolAlias");
final Future<ResultOfMyTask> myTask = threadPool.submit(callable);;

You should store reference to myTask and can query it from other threads to find out whether it's finished and what is the result.

Hope this helps...

Yuliia Ashomok
  • 8,336
  • 2
  • 60
  • 69
Yuriy Nakonechnyy
  • 3,742
  • 4
  • 29
  • 41
  • that's pretty much EXACTLY what I've got so far :) I'll let this question stay open a bit more for the case of anyone comes with another idea, but wanted to thank you already for taking the time to write such a nice answer. – Poni Jun 15 '12 at 19:57
  • 1
    Probably worth mentioning that `contextInitialized` and `contextDestroyed` methods should be located in a custom class that implements `ServletContextListener`, and that you tell Tomcat about that class in your `web.xml` like `path.to.your.YourServletListener` – RTF Nov 07 '15 at 11:28
  • @RTF yes, I mentioned that in items 1 and 2, however it may be not clear enough, so thanks for your addition – Yuriy Nakonechnyy Nov 09 '15 at 14:46
0

For your use case, you can utilize Timer and TimerTask available in java platform to execute a background task periodically.

import java.util.Timer;
import java.util.TimerTask;

TimerTask dbTask = new TimerTask() {
    @Override
    public void run() {
        // Perform Db call and do some action
    }
};

final long INTERVAL = 1000 * 60 * 5; // five minute interval

// Run the task at fixed interval
new Timer(true).scheduleAtFixedRate(dbTask, 0, INTERVAL);

Note, if a task takes more than five minutes to finish, subsequent tasks will not be executed in parallel. Instead they will wait in the queue and will be executed rapidly one after another when the previous one finishes.

You can wrap this code in a singleton class and invoke from the startup servlet.

Generally, its a good practice to perform these kinds of periodic background jobs outside of Servlet containers as they should be efficiently used serve HTTP requests.

sperumal
  • 1,499
  • 10
  • 14
  • 1
    The question asks for a solution with a thread pool. Timer uses a single background thread. ScheduledThreadPoolExecutor on the other hand ... – Sridhar Jun 15 '12 at 14:41
0

For a simple background-task, you don't need any kind of thread pool at all. All you need to do is:

  1. Launch a thread
  2. Check to see if the background process should stop
  3. Have it poll the database
  4. Store freshly-polled data somewhere accessible
  5. Check to see if the background process should stop
  6. Go to sleep
  7. Repeat steps #2-#7

Write a ServletContextListener that launches a thread to perform the above steps that you define in a class that implements Runnable. In the contextDestroyed method, set a flag that triggers the checks indicated above in #2 and #5 and then calls Thread.interrupt so your thread terminates.

Your background task should definitely not try to synchronously send messages to clients. Instead, notify waiting pollers using some other mechanism like Object.notify on a monitor (which doesn't really make any sense since you don't want to block clients checking the current status), update a timestamp of some kind, or just have the polling-clients check the current data available in #4.

Christopher Schultz
  • 20,221
  • 9
  • 60
  • 77
  • I would say that spawning threads in a web server is probably not a good idea: you could easily end up with hundreds of threads and then crash the server. A thread pool on the other hand does not have this issue, provides queuing of requests and avoids the overhead of creating new threads all the time. – Didier L Jul 18 '13 at 14:07
  • @DidierL So you don't want to start threads on a server, but your recommendation is to use a thread pool instead? Unless you have a (useless) thread pool of size=0, then you are creating threads. I recommended creating a single thread and using a queue of tasks. A thread pool is needless overhead unless there is a reason to maintain more than one thread. Resource management is not a problem if you manage the thread from a `ServletContextListener`. – Christopher Schultz Jul 18 '13 at 14:45
  • Well, I was suggesting *managing* a single thread instead of using a thread pool. ;) It's not hard to add a `pleaseStop` method to a thread. – Christopher Schultz Jul 18 '13 at 15:08
  • Yes, sorry I didn't figure you only wanted to work with a single thread. However in this particular case the user seemed to require processing several requests in parallel so it probably needs more, and a thread pool is probably the most reliable (handles thread crashes) and the simplest solution. – Didier L Jul 18 '13 at 15:14