Pregunta

En primer lugar, hay que decir que estoy bastante nuevo en el java.util.concurrent API, así que tal vez lo que estoy haciendo es totalmente erróneo.

¿Qué quiero hacer?

Tengo una aplicación Java que funciona básicamente 2 procesamiento separado (llamado myFirstProcess , mySecondProcess ), pero estos procesamiento debe ejecutarse al mismo tiempo.

Por lo tanto, he tratado de hacer lo siguiente:

public void startMyApplication() {
    ExecutorService executor = Executors.newFixedThreadPool(2);
    FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
    FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
    executor.execute(futureOne);
    executor.execute(futureTwo);
    while (!(futureOne.isDone() && futureTwo.isDone())) {
        try {
            // I wait until both processes are finished.
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    logger.info("Processing finished");
    executor.shutdown();
    // Do some processing on results
    ...
}

myFirstProcess y mySecondProcess son clases que implementan Callable<Object>, y donde toda su procesamiento se hace en el método call ().

Se está trabajando bastante bien, pero no estoy seguro de que es la forma correcta de hacerlo. Es una buena manera de hacer lo que quiero? Si no, ¿puede darme algunos consejos para mejorar mi código (y todavía mantenerlo tan simple como sea posible).

¿Fue útil?

Solución

Usted sería mejor usar el método get().

futureOne.get();
futureTwo.get();

Tanto de los que sirven para la notificación de la rosca que se haya completado el procesamiento, esto le ahorra la concurrida-espera-con-temporizador esté utilizando en el cual no es eficiente ni elegante.

Como beneficio adicional, usted tiene la get(long timeout, TimeUnit unit) API que le permite definir un tiempo máximo para el hilo a dormir y esperar una respuesta, y por lo demás sigue funcionando.

Vea la Java API para obtener más información.

Otros consejos

Los usos de FutureTask anteriormente son tolerables, pero definitivamente no es idiomática. En realidad estás pasando un adicional FutureTask torno con el que envió a la ExecutorService. Su FutureTask es tratado como un Runnable por el ExecutorService. Internamente, se envuelve su FutureTask-como-Runnable en una nueva FutureTask y lo devuelve a usted como Future<?>.

En su lugar, usted debe enviar sus casos Callable<Object> a un CompletionService. Se le caen dos Callables en medio de submit(Callable<V>), a continuación, dar la vuelta y llamar CompletionService#take() dos veces (una para cada Callable presentado). Esas llamadas se bloquean hasta que uno y luego el resto de tareas presentadas están completas.

Teniendo en cuenta que ya tiene una Executor en la mano, construir una nueva ExecutorCompletionService alrededor de ella y soltar sus tareas allí. No haga girar y espera el sueño; CompletionService#take() bloqueará hasta que una de sus tareas se han completado (o bien termina de ejecutarse o cancelado) o el hilo espera en take() se interrumpe.

La solución de Yuval está muy bien. Como alternativa también se puede hacer esto:

ExecutorService executor = Executors.newFixedThreadPool();
FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
executor.execute(futureOne);
executor.execute(futureTwo);
executor.shutdown();
try {
  executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
  // interrupted
}

¿Cuál es la ventaja de este enfoque? No hay mucha diferencia realmente, excepto que esta forma de detener el ejecutor acepta más tareas (se puede hacer eso en el otro sentido). Yo tiendo a preferir este idioma a pesar de que uno.

Además, si bien get () emite una excepción que puede terminar en una parte de su código asume que ambas tareas se llevan a cabo, lo que podría ser malo.

Puede utilizar invokeall (Colección ....) Método

package concurrent.threadPool;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class InvokeAll {

    public static void main(String[] args) throws Exception {
        ExecutorService service = Executors.newFixedThreadPool(5);
        List<Future<java.lang.String>> futureList = service.invokeAll(Arrays.asList(new Task1<String>(),new Task2<String>()));

        System.out.println(futureList.get(1).get());
        System.out.println(futureList.get(0).get());
    }

    private static class Task1<String> implements Callable<String>{

        @Override
        public String call() throws Exception {
            Thread.sleep(1000 * 10);
            return (String) "1000 * 5";
        }

    }

    private static class Task2<String> implements Callable<String>{

        @Override
        public String call() throws Exception {
            Thread.sleep(1000 * 2);
            int i=3;
            if(i==3)
                throw new RuntimeException("Its Wrong");
            return (String) "1000 * 2";
        }

    }
}

Es posible que desee utilizar un CyclicBarrier si usted está interesado en iniciar los subprocesos al mismo tiempo, o esperar a que termine y luego hacer algún procesamiento ulterior. Consulte el Javadoc para obtener más información.

Si sus futureTasks son más de 2, por favor considere [ListenableFuture][1].

  

Cuando varias operaciones deben comenzar tan pronto como otra operación   arranques - " abanico de salida " - ListenableFuture simplemente funciona: desencadena todos   las devoluciones de llamada solicitada. Con un poco más de trabajo, podemos " abanico de entrada ", o   desencadenar una ListenableFuture calcula para conseguir tan pronto como varios otros   Todos los futuros han terminado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top