2012-08-02 27 views
5

Necesito realizar algunas tareas (Principalmente Llamar múltiples URL externas con parámetros de solicitud y datos de lectura) concurrentemente en servlet java y enviar respuesta al usuario en unos pocos segundos. Estoy tratando de usar ExecutorService para lograr el mismo . Necesito cuatro FutureTasks creados en cada solicitud de usuario en el método doGet. Cada tarea se ejecuta durante alrededor de 5-10 segundos y el tiempo de respuesta total para el usuario es de alrededor de 15 segundos.ExecutorService en Java Servlet

¿Puede sugerir cuál de los siguientes diseños es mejor si utiliza ExecutorService en un servlet de Java?

1) (Creación newFixedThreadPool por solicitud y apagarlo ASAP)

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) (Creación newFixedThreadPool durante el servlet de inicio y apagarlo en servlet destruir)

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) (Creando newCachedThreadPool durante Servlet Init y cerrándolo en servlet destroy)

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

} 
+0

El contenedor crea y carga una única instancia del servlet. Y todas las solicitudes son procesadas por la misma instancia. Entonces, 'ExecutorService myThreadPool = null;' no es seguro. –

+0

¿Puede sugerir cómo declarar ExecutorService globalmente? – user1263019

Respuesta

1

La primera no debería ser una opción. La idea de un grupo de subprocesos (y probablemente cualquier grupo) es minimizar la sobrecarga y la memoria requerida para la construcción de los miembros del grupo (en este caso, los subprocesos de trabajo). Entonces, en general, las agrupaciones se deben ingresar cuando se inicia su aplicación y se destruye cuando se apaga.

En cuanto a la opción entre 2 y 3, compruebe la respuesta aceptada en la siguiente publicación. La respuesta explica la diferencia y luego puede decidir cuál satisface mejor sus necesidades: newcachedthreadpool-v-s-newfixedthreadpool

+0

Gracias por la respuesta. En el enlace sugeriste que newcachedthreadpool parece adecuado, ya que mis tareas están vinculadas a una solicitud http y se completan en pocos segundos. – user1263019

0

Crear y destruir un grupo de subprocesos para cada solicitud es una mala idea: demasiado caro.

Si tiene alguna forma de recordar qué solicitud HTTP está relacionada con cada tarea de recuperación de URL, elegiría un CachedThreadPool. Su capacidad de crecer y reducirse según demanda hará maravillas, ya que las tareas de obtención de URL son totalmente independientes y vinculadas a la red (a diferencia de la CPU o vinculadas a la memoria).

Además, envolvería el ThreadPool en un CompletionService, que puede notificarle cada vez que se realiza un trabajo, independientemente de su orden de presentación. Primero completado, primero notificado. Esto asegurará que no bloquees en un trabajo lento si los más rápidos ya están hechos.

CompletionService es fácil de usar: envuélvalo alrededor de un ThreadPool existente (newCachedThreadPool por ejemplo), envíele trabajos(), y luego restaure() los resultados. Tenga en cuenta que el método take() está bloqueando.

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

+0

Gracias por la respuesta. ¿Puede explicar "si tiene alguna forma de recordar qué solicitud HTTP está relacionada con cada tarea de búsqueda de URL"? ¿Hay alguna posibilidad de desajuste entre la tarea y la solicitud si uso cachedthreadpool?A pesar de que threadpool es el mismo para cada solicitud, la tarea será nueva para cada solicitud y puedo recuperarla con el método .get() una vez que se complete ¿verdad? – user1263019

+1

Hummm Cambié de opinión: un servicio de finalización requeriría que tuviera un bloque de hilos en el método take(), y luego volvería a enviar el Future resultante a la solicitud de origen, lo que sería bastante complicado. Será mejor que use invokeAll() en un ExecutorService estándar: envíele una lista de trabajos y le devuelve una lista de resultados. Más fácil y más eficiente. –

Cuestiones relacionadas