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

RTF*_*RTF 3 java multithreading tomcat servlets servlet-3.0

我的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
 }
Run Code Online (Sandbox Code Playgroud)

当我说安全时,我的意思是关于Web服务器的稳定性,我提出的内容是否存在任何内在的危险.正如@Burrman指出的那样,线程池在这里是个好主意,我会这样做.如果我正在使用线程池,那么我应该关注或需要解决的servlet容器是否存在任何其他潜在问题?

我想我正在考虑的是,例如,JDBC连接.我相信建议使用JNDI资源等进行设置,并使用Tomcat配置进行配置.是否有必要或建议产生任意线程,如我的例子?

kup*_*fic 6

首先,它看起来你正在修改result两个线程中的对象.这不是线程安全的,因为这些firstsecond线程可能彼此不可见或者servlet正在运行的线程可见.有关详细信息,请参阅此文章.

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

假设MyResult result影响response的对象(你要么加入resultresponse,它的影响的响应代码等).有几种方法可以解决这个问题.

  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<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
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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();
       }
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 同样出于**性能原因**我建议**在webapp**的**开始时**创建一个全局`ExecutorService`实例,并在webapp停止时关闭它,并且**在所有请求中使用它**在每个请求期间创建/关闭它.请查看我的答案http://stackoverflow.com/questions/11050186/tomcat-6-thread-pool-for-asynchronous-processing/11053152#11053152,了解如何根据整个webapp创建全局线程池.这不应该导致任何问题,因为`ExecutorService`是完全线程安全的,因此它可以根据需要从尽可能多的线程中使用 (2认同)