一尘不染

Java servlet生成线程以满足请求是否安全?

tomcat

Java(Tomcat
8)Web服务器响应HTTP请求生成线程是否安全?我看到有人在帖子和论坛上说这绝对不错,而另一些人则说不行

我的用例将是这样的:

 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
 }

当我说“安全”时,我的意思是关于网络服务器稳定性的建议有天生的危险。正如@Burrman指出的那样,在这里线程池是个好主意,我将这样做。如果我正在使用线程池,那么我应该关注或需要解决的servlet容器是否还有其他潜在问题?

我想我正在考虑的是例如JDBC连接。我相信建议使用JNDI资源等进行设置,并使用Tomcat
config进行配置。像我的示例中那样,是否有必要或建议使用类似的方法来生成任意线程?


阅读 191

收藏
2020-06-16

共1个答案

一尘不染

首先,看起来您正在修改result两个线程中的对象。这不是线程安全的,因为firstsecond线程之间的操作可能彼此之间或运行servlet的线程之间不可见。有关更多信息,请参见本文

其次,如果您正在其他线程中修改响应,则不会,这将是不安全的。退出doGet方法后,应考虑发送的响应。在您的示例中,有可能在这两个线程运行之前将响应发送回客户端。

假设MyResult result影响了response对象(您将添加result到中response,它正在影响响应代码,等等)。有几种方法可以解决此问题。

  1. 使用ExecutorServiceFuture

    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 f1 = executor.submit(new Callable() {
    @Override
    public Result1 call() throws Exception {
    // do expensive stuff here.
    return result;
    }
    });

    Future f2 = executor.submit(new Callable() {
    @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. 一种更高级的技术是异步处理。如果您的请求需要很长时间才能处理,则使用异步处理是一个好主意。它不会提高任何一个请求的延迟,但是它确实允许Tomcat在任何给定时间点处理更多请求。

一个简单的例子是:

    @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();
   }
}
2020-06-16