Java Servlet中的ExecutorService

6
我需要在Java servlet中同时执行一些任务(主要是调用多个带请求参数的外部URL并读取数据),并在几秒钟内向用户发送响应。我正在尝试使用ExecutorService来实现这一目标。我需要在doGet方法中为每个用户请求创建四个FutureTasks。每个任务运行时间大约为5-10秒,总响应时间为15秒左右。
请问在使用ExecutorService时,以下哪种设计更好?
1) (每个请求创建一个新的FixedThreadPool,并尽快关闭它)
public class MyTestServlet extends HttpServlet
{

    ExecutorService myThreadPool = null;

    public void init()
    {
          super.init();

    }
    protected void doGet(HttpServletRequest request,HttpServletResponse response)
    {

        myThreadPool = Executors.newFixedThreadPool(4);
        taskOne   = myThreadPool.submit();
        taskTwo   = myThreadPool.submit();        
        taskThree = myThreadPool.submit();
        taskFour  = myThreadPool.submit();

        ...
        ...

        taskOne.get();
        taskTwo.get();
        taskThree.get();
        taskFour.get();

        ...

        myThreadPool.shutdown();


    }

     public void destroy()
     {

         super.destroy();
     }

}

2) (在Servlet初始化期间创建新的FixedThreadPool,并在servlet销毁时关闭它)

public class MyTestServlet extends HttpServlet
{

    ExecutorService myThreadPool = null;

    public void init()
    {
      super.init();
          //What should be the value of fixed thread pool so that it can handle multiple   user requests without wait???
          myThreadPool = Executors.newFixedThreadPool(20);

    }
    protected void doGet(HttpServletRequest request,HttpServletResponse response)
    {


        taskOne   = myThreadPool.submit();
        taskTwo   = myThreadPool.submit();        
        taskThree = myThreadPool.submit();
        taskFour  = myThreadPool.submit();

        ...
        ...

        taskOne.get();
        taskTwo.get();
        taskThree.get();
        taskFour.get();

        ...



    }

     public void destroy()
     {

          super.destroy();
          myThreadPool.shutdown();
     }

}

3) 在Servlet Init期间创建newCachedThreadPool,并在servlet销毁时关闭它

public class MyTestServlet extends HttpServlet
{

      ExecutorService myThreadPool = null;

      public void init()
      {
        super.init();
            myThreadPool = Executors.newCachedThreadPool();

      }
      protected void doGet(HttpServletRequest request,HttpServletResponse response)
      {


          taskOne   = myThreadPool.submit();
          taskTwo   = myThreadPool.submit();        
          taskThree = myThreadPool.submit();
          taskFour  = myThreadPool.submit();

          ...
          ...

          taskOne.get();
          taskTwo.get();
          taskThree.get();
          taskFour.get();

          ...




     }

     public void destroy()
     {

            super.destroy();
            myThreadPool.shutdown();
      }

}

容器创建并加载一个servlet的单个实例。所有请求都由同一个实例处理。因此,ExecutorService myThreadPool = null; 不安全。 - Bhesh Gurung
请问如何全局声明ExecutorService? - user1263019
2个回答

1
第一种选择不应该是一个选项。线程池(也许任何池)的想法是最小化池成员的构造开销和内存消耗(在这种情况下,工作线程)。因此,通常应在启动应用程序时初始化池,并在关闭时销毁池。
至于2和3之间的选择,请在以下帖子中查看已接受的答案。该答案解释了区别,然后您可以决定哪个更适合您的需求 : newcachedthreadpool-v-s-newfixedthreadpool

谢谢你的回答。根据你提出的链接,newcachedthreadpool似乎适合我的任务,因为它们与http请求相关,并且在几秒钟内完成。 - user1263019

0

为每个请求创建和销毁线程池是一个不好的主意:太昂贵了。

如果您有一些方法来记住每个URL获取任务与哪个HTTP请求相关联,我建议使用CachedThreadPool。它的按需增长和收缩能力会产生奇迹,因为URL获取任务完全独立且受网络限制(与CPU或内存限制相反)。

此外,我会将ThreadPool包装在CompletionService中,它可以在作业完成时通知您,而不管其提交顺序如何。首先完成,首先通知。这将确保如果更快的作业已经完成,则不会阻塞慢速作业。

CompletionService很容易使用:将其包装在现有的ThreadPool(例如newCachedThreadPool)周围,向其中提交()作业,然后将结果take()回来。请注意,take()方法是阻塞的。

http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CompletionService.html


谢谢你的回答。你能否详细说明一下“如果你有一些方法来记住每个URL获取任务与哪个HTTP请求相关”?如果我使用cachedthreadpool,是否存在任务和请求不匹配的可能性?即使线程池对于每个请求都是相同的,但是任务对于每个请求都是新的,我可以在它完成后使用.get()方法检索对应的任务,对吗? - user1263019
1
嗯,我改变了想法 - 完成服务将要求您在take()方法上有一个线程块,然后重新分配结果Future给原始请求,这将是相当复杂的。您最好使用标准ExecutorService上的invokeAll():向其提交作业列表,它会返回结果列表。更简单,更高效。 - Olivier Croisier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接