2012-03-01 8 views
15

He buscado pero no he encontrado una respuesta, así que quería confirmar esto con certeza.Java Thread Pools/Executor Service y wait() s: ¿qué ocurre con los hilos y la cola de tareas?

decir que tengo un grupo de subprocesos tamaño fijo - ExecutorService pool = Executors.newFixedThreadPool(5);

y tengo algo de código:

pool.execute(new Runnable(){ 
    try{ 
     Object waitForMe = doSomethingAndGetObjectToWaitFor(); 
     waitForMe.wait(); 
     doSomethingElse(); 
    }catch(Exception e){ throw new RunTimeException(e) } 

}); 

supongamos que el código anterior se llama un par de 100 veces. Solo hay 5 subprocesos en el grupo (por lo que solo 5 de los enunciados anteriores deben estar en vivo en un punto). Supongamos también que wait() está en un objeto que realiza algunas llamadas de E/S a un tercero y espera una devolución de llamada cuando se completa la operación, por lo que demorará un tiempo en completarse.

Ahora mi pregunta es ¿cuál es el comportamiento cuando una de estas tareas llega a wait(), la tarea pasa a la suspensión y luego el hilo del grupo de subprocesos retira otra tarea de la cola y comienza a ejecutarla?

Si la tarea que está esperando va a dormir, ¿qué sucede cuando se obtiene un notify() y se despierta? ¿El hilo vuelve a la cola (en la parte delantera o posterior) para el grupo de subprocesos y espera hasta que uno de los 5 subprocesos pueda seguir ejecutándolo (es decir, llame al doSomethingelse())? ¿O el hilo que lo estaba ejecutando también se va a dormir, es decir, uno de los 5 subprocesos del ejecutor está esperando la tarea (esto es lo que asumo)? ¿O el ejecutor retoma una nueva tarea y simplemente se interrumpe cuando la primera tarea regresa de wait()?

Respuesta

16

wait() es una operación de bloqueo:

Hace que el hilo actual que esperar hasta que otro hilo invoca el método notify() o la notifyAll()

Esto significa que el hilo en la piscina esperará, pero desde afuera parece que la tarea actual toma mucho tiempo en completarse. Esto también significa que si se ejecutan 5 tareas y todas ellas wait(), el Executor no puede manejar las tareas restantes que, ekhem, esperan en la cola.

Cierto, el subproceso ejecutor se queda dormido permitiendo que otros subprocesos conmuten y consuman CPU (para que pueda tener cientos de subprocesos esperando al mismo tiempo y su sistema sigue siendo sensible) pero el subproceso "no se puede usar" y obstruido.

Otra característica interesante es que interrumpe - si el hilo espera algo o duerme puede interrumpirlo. Tenga en cuenta que wait() y Thread.sleep() declaran InterruptedException. Con ExecutorService puede aprovechar esto simplemente llamando al: future.cancel() (future es el objeto que recibió a cambio al enviar la tarea al ExecutorService).

Finalmente, creo que debería rediseñar su solución. En lugar de esperar activamente por un sistema externo para terminar, proporcionar una API con devoluciones de llamada:

pool.execute(new Runnable(){ 
    try{ 
     doSomethingAndCallMeBackWhenItsDone(new Callback() { 
      public void done() { 
       doSomethingElse(); 
      } 
     }); 
    }catch(Exception e){ throw new RunTimeException(e) } 

}); 

De esta manera la API del sistema externo, simplemente le notifique cuando los resultados están listos y que no tendrá que esperar y bloquear ExecutorService .Por último, si doSomethingElse() toma mucho tiempo, es posible que incluso decidir programarlo así en lugar de utilizar terceros externa de E/S hilo:

pool.execute(new Runnable(){ 
    try{ 
     doSomethingAndCallMeBackWhenItIsDone(new Callback() { 
      public void done() { 
       pool.submit(new Callbale<Void>() { 
        public Void call() { 
         doSomethingElse(); 
        } 
       } 
      } 
     }); 
    }catch(Exception e){ throw new RunTimeException(e) } 

}); 

ACTUALIZACIÓN: usted está preguntando qué hacer con los tiempos de espera? Aquí es mi idea:

pool.execute(new Runnable(){ 
    try{ 
     doSomethingAndCallMeBackWhenItsDone(new Callback() { 
      public void done() { 
       doSomethingElse(); 
      } 
      public void timeout() { 
       //opps! 
      } 
     }); 
    }catch(Exception e){ throw new RunTimeException(e) } 

}); 

supongo que se puede poner en práctica el tiempo de espera en el lado de terceros y si el tiempo de espera se produce allí, simplemente llamo timeout() método.

+1

¡Gracias por la gran respuesta! Con la opción de devolución de llamada, ¿cuál sería la mejor manera de establecer un tiempo de espera (por ejemplo, desea esperar x segundos para una devolución de llamada si nada arroja un error). La única forma en que puedo pensar es tomar nota del milisegundo actual del sistema y mantenerlo en una lista y hacer que otro subproceso controle la lista para las llamadas que han excedido la hora actual y desencadenan un error. Necesito encontrar un buen libro que trate sobre simultaneidad, devolución de llamadas, etc. ¡Gracias de nuevo! – NightWolf

+1

@NightWolf: wrt a los tiempos de espera ver mi respuesta actualizada. Cuando se trata de un buen libro, [Concurrencia de Java en la práctica] (http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601) es una necesidad. –

+0

Gracias a montones por la actualización y el enlace del libro, buena idea. Tristemente no tengo ningún control sobre el lado de los terceros. – NightWolf

1

El wait() no puede saber nada sobre la piscina de la banda de rodadura. Y el grupo de subprocesos no puede saber nada sobre el wait(). Entonces ellos no pueden interactuar de ninguna manera.

Funcionan de la forma habitual: el wait() es una operación de bloqueo de larga ejecución, el grupo de subprocesos es solo una cola de ejecutables que se ejecuta en un grupo limitado de subprocesos.

0

Me gustaría comentar la respuesta de Tomasz, pero mi reputación no lo permite (aún), lo siento.

sé que la pregunta es viejo, pero para la gente que aún así terminar de leer esta página, echar un vistazo a futuro y sobre todo de ListenableFuture guayaba que le permite registrar las devoluciones de llamada y el futuro de la cadena junto con, precisamente, con el fin de no bloquear su hilo (y así liberar el hilo de vuelta a la piscina para otra cosa para usarlo).

0

Todos los 5 hilos serán bloqueados y la aplicación estará en estado no productivo.

Agregando a Tomasz respuesta, me gustaría poner en práctica el tiempo de espera mecanismo de la siguiente manera.

  Future<Long> futureResult = service.execute(myCallable); 
      Long result = null; 
      try{ 
       result = futureResult.get(5000, TimeUnit.MILLISECONDS); 
      }catch(TimeoutException e){ 
       System.out.println("Time out after 5 seconds"); 
       futureResult.cancel(true); 
      }catch(InterruptedException ie){ 
       System.out.println("Error: Interrupted"); 
      }catch(ExecutionException ee){ 
       System.out.println("Error: Execution interrupted"); 
      } 

Aparte de TimeoutException, puede cancelar futuro durante InterruptedException & ExecutionException. Si utiliza submit() en lugar de execute(), InterruptedException & ExecutionException se tragará en el marco mismo.

Cuestiones relacionadas