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

时间:2015-11-06 13:44:26

标签: java multithreading tomcat servlets servlet-3.0

我的Java(Tomcat 8)Web服务器是否可以安全地生成线程以响应HTTP请求?我看到帖子和论坛有些人说absolutely fine,其他人说not to do it

我的用例是这样的:

 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
 }

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

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

5 个答案:

答案 0 :(得分:6)

首先,它看起来你正在修改两个线程中的$args = array( 'post_type' => array( 'videos' ), 'orderby' => 'rand', 'posts_per_page' => 5, 'meta_query' => array( array( 'key' => 'post_title', 'value' => 'YouTube.com', 'compare' => 'IN', ) ) ); 对象。这不是线程安全的,因为resultfirst线程可能彼此不可见或者servlet正在运行的线程可见。有关详细信息,请参阅this article

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

假设doGet影响MyResult result对象(您要将response添加到result,它会影响响应代码,等等)。有几种方法可以解决这个问题。

  1. 使用responseExecutorService

    Future
  2. 更先进的技术是Asynchronous Processing。如果您的请求需要很长时间来处理,则使用异步处理是一个好主意。它不会改善任何一个请求的延迟,但它确实允许Tomcat在任何给定的时间点处理更多请求。

    一个简单的例子是:

    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
    }
    

答案 1 :(得分:3)

另外,除了A的优秀答案之外,我真的建议你考虑一下你的结果计算是否可以用 map / filter / group operations 来表达在列表或地图上,因为在90%的情况下,它可以这样做。

如果情况确实如此,我建议您使用this official tutorial

中概述的 Java 8 kuporific功能

如果您有兴趣是否/如何以这种方式表达您的计算,请提出单独的问题

另外,回答你的初始问题 - 在任何地方(包括servlet)生成线程来并行计算都非常好,但我真的建议测量性能之前和之后优化后主要是因为in this superb article

所述的原因

答案 2 :(得分:0)

原则上,是的。但是你必须密切关注你产生的线程总数,以确保你没有使用太多的资源。

使用线程池可以帮助控制线程数。

答案 3 :(得分:0)

在我看来

  1. 这个想法没有感觉线程是否应该在同一个请求中生效(cit。以满足请求
  2. 当在下一个请求中(特别是通过AJAX)可见线程(运行/结束)的效果时,
  3. 可能感知(如果正确完成)。大多数servlet框架都有办法如何以“正统的方式”进行长时间的操作。
  4. 要明确:这不是执行典型的日常AJAX任务的常规方法(必须将设计划分为“主要”请求和下一个孩子ajax请求,使用您的框架),但AJAX可以呈现后台长线程的状态。

    在我的构思中,线程或等价物可以从少数/罕见的请求开始,远远低于限制等,但如果是从每个请求完成,那么设计是非常糟糕的。

答案 4 :(得分:-1)

JEE服务器在线程局部变量中存储一些信息。例如。 JAAS的安全上下文,JTA事务,...新的普通java线程无法访问此类信息。 AsyncContext和JEE ExecuterService已集成到服务器中,可以将请求状态透明地传播到托管线程。