2012-08-10 10 views
6

Estoy tratando de entender cómo un Callable puede devolver un valor cuando se ejecuta en un hilo diferente.¿Cómo funciona una Llamada bajo el capó? ¿Cómo es posible que un objeto invocable devuelva un valor?

Busco en las clases Executors, AbstractExecutorService, ThreadPoolExecutor y FutureTask, todos disponibles en java.util.concurrent paquete.

Crea un objeto ExecutorService llamando a un método en Ejecutores (por ejemplo, newSingleThreadExecutor()). Luego puede pasar un objeto invocable con ExecutorService.submit(Callable c).

Dado que el método call() se ejecuta mediante un subproceso proporcionado por el ExecutorService, ¿dónde el objeto devuelto "salta" de nuevo al hilo de llamada?

Look en este ejemplo sencillo:

1 ExecutorService executor = Executors.newSingleThreadExecutor(); 
2 public static void main(String[] args) { 
3  Integer i = executor.submit(new Callable<Integer>(){ 
4   public Integer call() throws Exception { 
5    return 10; 
6   } 
7  }).get(); 
8  System.out.print("Returns: " + i + " Thread: " + Thread.currentThread.getName()); 
9  // prints "10 main" 
10 } 

¿Cómo es posible que el número entero en el método de llamada, que está dirigido por un hilo separado, se devuelve al objeto entero (fila 3) por lo que puede ser impreso por la declaración System.out en el hilo principal (fila 7)?

¿No es posible ejecutar el hilo principal antes de que el ExecutorService haya ejecutado su hilo, de modo que el System.out statement imprime nulo?

+0

Existen algunos errores de compilación en este código; por ejemplo, 'executor.submit' devuelve un futuro, no un entero, y' currentThread' es un método que debe invocarse. Si a alguien le interesa ver un ejemplo que funcione, consulte http://ideone.com/myoMB –

+0

Disculpe, estaba escribiendo ese código a mano. :-) Echaré un vistazo a tu ejemplo. – Rox

+0

Oh no hay problema, solo estaba tratando de ser útil. Es una buena pregunta. +1 –

Respuesta

8

¿Cómo es posible que el número entero en el método de llamada, que está dirigido por un hilo separado, se devuelve al objeto Integer

ExecutorService.submit(...) hace no devolver el objeto de call() pero devuelve un Future<Integer> y puede usar el método Future.get() para obtener ese objeto. Vea el código de ejemplo a continuación.

¿No es posible ejecutar el hilo principal antes de que el ExecutorService ejecute su hilo, de modo que la sentencia System.out imprima nulo?

No, el método get() espera a que el trabajo finalice. Si call() devolvió nulo, entonces get() de lo contrario devolverá (e imprimirá) 10 garantizado.

Future<Integer> future = executor.submit(new Callable<Integer>(){ 
    public Integer call() throws Exception { 
     return 10; 
    } 
}); 
try { 
    // get() waits for the job to finish before returning the value 
    // it also might throw an exception if your call() threw 
    Integer i = future.get(); 
    ... 
} catch (ExecutionException e) { 
    // this cause exception is the one thrown by the call() method 
    Exception cause = e.getCause(); 
    ... 
} 
+0

¡Gracias por tu respuesta! ¿Dónde ocurre la espera? Debe ser una sincronización para que el hilo de llamada (por ejemplo, el hilo principal) pueda esperar a que ExecutorService complete su tarea. He buscado en FutureTask y en ThreadPoolExecutor y cannod veo esa sincronización. – Rox

+0

Funciona así, que el hilo principal ingresa al método 'Future.get()' antes de que ExecutorService ingrese el método 'RunnableFuture.run()', pero el método principal tiene que esperar a que ExecutorService complete la ejecución () método y cuando tiene, notifys el hilo principal para continuar con el método get()? – Rox

+2

El 'Future.get()' hace la espera. Dentro de 'FutureTask' creo que' acquireSharedInterruptibly (0) 'y luego' 'doAcquireSharedInterruptibly (...)' hace la sincronización y espera. La sincronización se realiza con 'volátil'. – Gray

4

echar un vistazo a ExecutorService.submit() método:

<T> Future<T> submit(Callable<T> task): envía una tarea de retorno de valor para su ejecución y devuelve un futuro que representa los resultados pendientes de la tarea. El método de obtención del futuro devolverá el resultado de la tarea una vez que se complete con éxito. Si desea bloquear de inmediato a la espera de una tarea, puede utilizar las construcciones de la forma result = exec.submit(aCallable).get();


P. ¿o no posible que el hilo principal que se ejecute antes de la ExecutorService tiene ejecutar su hilo, para que la instrucción System.out imprima nulo?

->Future<T>.get() Espera si es necesario para que se complete el cálculo, y luego recupera el resultado.

+0

Gracias por responder. Mira mi pregunta a la respuesta de Gray. ¿Dónde tiene lugar la sincronización para que el hilo principal pueda esperar a que ExecutorService complete su tarea? – Rox

Cuestiones relacionadas