Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
674 views
in Technique[技术] by (71.8m points)

multithreading - Is it safe for a Java servlet to spawn threads in order to satisfy a request?

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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();
       }
    }
    

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...