3

Is it safe for my Java (Tomcat 8) web server to spawn threads in response to a HTTP request? I'm seeing posts and forums where some people say it's absolutely fine, and others say not to do it.

My use case would be something like this:

 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ...
    ...
    final MyResult res = new MyResult();
    Thread first = new Thread(new Runnable() {
         @Override
         public void run() {
             // put this into res
         }
     });
     Thread second = new Thread(new Runnable() {
         @Override
         public void run() {
             // put that into res
         }
     });
     first.start();
     second.start();
     first.join(10000);
     second.join(10000);

     // return res
 }

When I say safe, I mean is there anything inherently dangerous about what I'm proposing with regards to the stability of the web server. As @Burrman points out, a thread pool is good idea here, and I will do that. If I am using a thread pool, is there then any other potential issues with the servlet container that I should be concerned about or need to address?

I suppose what I'm thinking about is, for example, JDBC connections. I believe it's recommended to set that up using JNDI resource etc. and configuring that with Tomcat config. Is anything like that necessary or recommended for spawning arbitrary threads like in my example?

Community
  • 1
  • 1
RTF
  • 6,214
  • 12
  • 64
  • 132
  • Maybe you should define what your understanding of 'safe' is in this context. There are various concerns like computational resources, memory usage, thread safety... – hotzst Nov 06 '15 at 13:57
  • Related/dupe: http://stackoverflow.com/questions/7499534/spawning-threads-in-a-jsf-managed-bean-for-scheduled-tasks-using-a-timer and http://stackoverflow.com/questions/6149919/is-it-safe-to-start-a-new-thread-in-a-jsf-managed-bean – BalusC Nov 06 '15 at 14:50
  • This is the very first topic of discussion in the book "Java Concurrency in practice" – Skynet Sep 26 '16 at 16:22

5 Answers5

6

First, it looks you're modifying the result object in both threads. This is not thread safe because what the first and second threads do might not be visible to each other or to the thread the servlet is running on. See this article for more info.

Second, if you are modifying the response in these other threads, no, this will not be safe. Once you exit the doGet method, you should consider the response sent. In your example, there's a chance the response will get sent back to the client before those two threads have run.

Suppose the MyResult result affects the response object (you're either adding the result to the response, it's affecting the response code, etc). There are a few ways to handle this.

  1. Use ExecutorService and Future:

    public void doGet(HttpServletRequest request, HttpServletResponse response) {
       // Creating a new ExecutorService for illustrative purposes.
       // As mentioned in comments, it is better to create a global 
       // instance of ExecutorService and use it in all servlets. 
       ExecutorService executor = Executors.newFixedThreadPool(2);
    
       Future<Result1> f1 = executor.submit(new Callable<Result1>() {
          @Override
           public Result1 call() throws Exception {
              // do expensive stuff here.
              return result;
          }
       });
    
       Future<Result2> f2 = executor.submit(new Callable<Result2>() {
          @Override
          public Result2 call() throws Exception {
             // do expensive stuff here.
             return result;
          }
       });
    
       // shutdown allows the executor to clean up its threads. 
       // Also prevents more Callables/Runnables from being submitted.
       executor.shutdown();
    
       // The call to .get() will block until the executor has
       // completed executing the Callable.
       Result1 r1 = f1.get();
       Result2 r2 = f2.get();
       MyResult result = new MyResult();
       // add r1 and r2 to result.
       // modify response based on result
    }
    
  2. A more advanced technique is Asynchronous Processing. Using async processing is a good idea if your requests take a long time to process. It does not improve the latency of any one request, but it does allow Tomcat to handle more requests at any given point in time.

    A simple example would be:

    @WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
    // Rather than @WebServlet, you can also specify these parameters in web.xml    
    public class AsyncServlet extends HttpServlet {
       @Override
       public void doGet(HttpServletRequest request, HttpServletResponse response) {
          response.setContentType("text/html;charset=UTF-8");
          final AsyncContext acontext = request.startAsync();
          acontext.start(new Runnable() {
             public void run() {
                // perform time consuming steps here.
                acontext.complete();
       }
    }
    
kuporific
  • 10,053
  • 3
  • 42
  • 46
  • Should I be launching `acontext.start(new Runnable...` twice? Once for each of the two threads that I need to run? – RTF Nov 06 '15 at 14:31
  • @RTF Instead of using raw `Thread` objects, use an `ExecutorService` instead. I've added the basics to my answer. I know it's a lot of information all at once... – kuporific Nov 06 '15 at 14:43
  • I don't have much experience with thread pools, but if I'm creating a new ExecutorService every time a server request is being processed, and then shutting it down before the server response is returned, does that not defeat the purpose of having a thread pool at all? Should I not be defining the ExecutorService statically and giving it a max pool of, for example, 30 threads? – RTF Nov 06 '15 at 14:50
  • 2
    Also for **performance reasons** I would advise to **create** one global `ExecutorService` instance at the **start of webapp** and shutdown it when webapp stops and just **use it in all requests** instead of creating / shutting it down during each request. Please check my answer here http://stackoverflow.com/questions/11050186/tomcat-6-thread-pool-for-asynchronous-processing/11053152#11053152 to see how to create global thread pool per whole webapp. This shouldn't cause any issues, because `ExecutorService` is totally thread-safe so it can be used from as many threads as needed – Yuriy Nakonechnyy Nov 06 '15 at 15:07
3

Also, besides excellent answer of kuporific I really advise you to think if your result computation could be expressed in terms of map / filter / group operations on lists or maps, because in 90% of cases it's possible to do so.

And if that's the case I would really advise you to use Java 8 Stream.parallelStream functionality as outlined in this official tutorial

Please ask separate question if you are interested if/how it's possible to express your computation in that way

Also, answering your initial question - it's perfectly fine to spawn threads to parallelise your computation anywhere (including servlets), however I would really advise to measure performance before and after optimization mainly because of reasons described in this superb article

Yuriy Nakonechnyy
  • 3,742
  • 4
  • 29
  • 41
  • Yes, I parallelised computations in this way many times with success and it's really fun, besides that `Java 8` took serious step on parallelism so you definitely should check this out :) However I just edited my answer to add some cautions, because such optmizations often tend only to make code complex rather than actually decrease execution time. – Yuriy Nakonechnyy Nov 06 '15 at 15:39
  • I decided to go with the thread pool and Future/Callable solution for now. That parallellism tutorial is bookmarked though, I'll definitely be taking a look at some point. – RTF Nov 07 '15 at 10:56
  • @RTF yes, it makes sense to use `ExecutorService/Future/Callable` approach as it'll allow for most flexible computation scenarios to be implemented – Yuriy Nakonechnyy Nov 09 '15 at 14:51
0

In principle, yes. But you have to keep an eye on the total number of threads you are spawning to make sure you're not using too many resources.

Using a thread pool can help keep your number of threads under control.

Buurman
  • 1,914
  • 17
  • 26
  • It looks like OP wants to modify the response in his threads, which is not safe to do because the response might be sent to the client before these threads run. – kuporific Nov 06 '15 at 14:23
  • What's the problem with that ? Stupid example: receive a request, spawn few threads to call different remote services, you'll only have to wait until the slowest thread has completed its work, add whathever you need to the response, not saying it's clean but I can't see the problem. – Emanuele Ivaldi Nov 06 '15 at 14:32
  • 1
    It's absolutely not safe in the way the OP intented. OP basically spawns manually crafted unmanaged threads in a managed container. This is recipe for trouble. – BalusC Nov 06 '15 at 14:50
  • 100% agree with you, but that has nothing to do with the problem of completing the tasks before the response is sent back and that's what I replyed to – Emanuele Ivaldi Nov 06 '15 at 15:00
  • @EmanueleI: I didn't respond to you, I just responded to the current post. Otherwise I'd have `@notified` you. – BalusC Nov 06 '15 at 15:05
  • @BalusC deceptive flat thread (lol, it's just me being silly), got it, my bad! – Emanuele Ivaldi Nov 06 '15 at 15:07
0

In my opinion

  1. this idea has not sense if thread should give effect in same request (cit. in order to satisfy a request)
  2. may have sense (if done properly) when effect of thread (running / ended) will be visible in next requests (especially via AJAX). Most servlet frameworks have way how to do "in orthodox way" long operations.

To be clear: this is not normal way to do typical everyday AJAX tasks (must divide design to "main" request and next children ajax requests, using your framework), but AJAX can present state of background long thread.

In my conception Thread or equivalent can be started from few/rare request, far below limits etc, but if it is done from every request, it is very bad design.

Jacek Cz
  • 1,872
  • 1
  • 15
  • 22
-1

JEE server stores some information in thread local variables. E.g. security context for JAAS, JTA transaction, ... New plain java thread has no access to such information. AsyncContext and JEE ExecuterService are integrated into server and can transparently spread request state to managed threads.

sibnick
  • 3,995
  • 20
  • 20