2011-08-03 18 views
5

Tengo un singleThreadExecutor para ejecutar las tareas que le presento en orden serial, es decir, una tarea tras otra, sin ejecución paralela.¿Cómo llegar al estado de ejecución de FutureTask?

tengo ejecutable, que es algo como esto

MyRunnable implements Runnable { 

@Override 
public void run() { 
    try { 
     Thread.sleep(30000); 
    } catch (InterruptedException e1) { 
     e1.printStackTrace(); 
    } 

}

Cuando envío, por ejemplo, tres casos de MyRunnable al ejecutor solo hilo antes mencionado, yo esperaría a tener la primera ejecución de tareas y debido a Thread.sleep tiene su hilo de ejecución en TIMED_WAITING (puedo estar equivocado sobre el estado específico). Las otras dos tareas no deberían tener hilos asignados para ejecutarlas, al menos no hasta que la primera tarea haya finalizado.

Así que mi pregunta es cómo obtener este estado a través de la API FutureTask o de alguna manera llegar al hilo que está ejecutando la tarea (si no existe dicho hilo, la tarea está esperando ser ejecutada o pendiente) y obtener su estado o tal vez por otros medios?

FutureTask solo define los métodos isCanceled() e isDone(), pero estos no son suficientes para describir todos los posibles estados de ejecución de la Tarea.

+2

la pregunta es: ¿por qué necesita algo además de isDone()? – bestsss

Respuesta

2

Se podría añadir un método getThread()-MyRunnable que produce el Thread ejecutar el método run().

que sugeriría la adición de una variable de instancia como esta (debe ser volatile para garantizar la corrección):

private volatile Thread myThread; 

hacer esto antes de la try bloque:

myThread = Thread.currentThread(); 

y añadir un bloque finally con este :

myThread = null; 

Entonces usted podría llamar:

final Thread theThread = myRunnable.getThread(); 
if (theThread != null) { 
    System.out.println(theThread.getState()); 
} 

para unos MyRunnable.

null es un resultado ambiguo en este momento, es decir, "no se ha ejecutado" o "se completó". Basta con añadir un método que indica si la operación se ha completado:

public boolean isDone() { 
    return done; 
} 

Por supuesto, se necesita de una variable de instancia para registrar este estado:

private volatile boolean done; 

Y ponemos a true en el bloque finally (Probablemente antes de establecer el hilo en null, hay un poco de una condición de carrera allí porque hay dos valores que capturan el estado de una cosa. En particular, con este enfoque se puede observar isDone() == true y getThread() != null.Se podría mitigar esto por tener un objeto lock para transiciones de estado y sincronizar en él cuando se cambia una o ambas variables de estado):

done = true; 

Tenga en cuenta que todavía no hay ningún guardia que prohíbe una sola MyRunnable se presenten simultáneamente a dos o más hilos. Sé que dices que no estás haciendo esto ... hoy :) Múltiples ejecuciones simultáneas conducirán a un estado corrupto con alta probabilidad. Puede poner alguna protección mutua exclusiva (como simplemente escribir synchronized en el método run()) al comienzo del método de ejecución para asegurarse de que solo se está ejecutando una sola ejecución en un momento dado.

+0

Me gusta este enfoque con el campo de espera Thread.currentThread. Sin embargo, para cubrir el conjunto completo de estados es necesaria una combinación de esto y la solución de Mark Peters. Porque si no hay un ActualThread asignado a un runnable dado, esto podría significar que el ejecutable aún está pendiente de ejecución o que ya se ha ejecutado, por lo que se necesitarían flags adicionales, como por ejemplo hasFinishedExecution. De todos modos, marqué esto como "respuesta aceptada", porque el estado de Thread refleja el estado de ejecución real de un ejecutable de forma más nativa. – Svilen

+0

actualizado para incluir esta preocupación. –

+0

¡Genial! Gracias por el ejemplo completo. – Svilen

3

Puede envolver todo lo que envíe a este servicio en un Runnable que registra cuando se ingresa su método de ejecución.

public class RecordingRunnable implements Runnable { 
    private final Runnable actualTask; 
    private volatile boolean isRunning = false; 
    //constructor, etc 

    public void run() { 
     isRunning = true; 
     actualTask.run(); 
     isRunning = false; 
    } 

    public boolean isRunning() { 
     return isRunning; 
    } 
} 
+1

como cualquier resultado concurrente isRunning() no tiene ningún significado real ya que verificarlo es una carrera de datos efectiva. 'java.util.concurrent.Future' tiene' isDone() 'que puede no flip-flop' false-true-false' y una vez 'verdadero' sigue siendo así. – bestsss

+1

@best: También podría usar fácilmente un booleano como 'hasStarted' y solo registrar cuando se ingrese el método de ejecución, o usar un' enum' que cubra todos los estados. Este ejemplo fue solo para la demostración del concepto de envoltura. –

1

Si quería ser muy cuidadoso, FutureTask rastrea los estados READY, RUNNING, RAN y CANCELLED internamente. Puede crear una copia de esta clase y agregar un descriptor de acceso para el estado. Luego, anule AbstractExecutorService.newTaskFor(Runnable) para envolverlo usando su CustomFutureTask (la clase interna es private, por lo que no funcionará la subclasificación).

La implementación predeterminada de newTaskFor(Runnable) es muy simple:

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { 
    return new FutureTask<T>(runnable, value); 
} 

por lo que no sería un gran problema para anularlo.

0

Dado que FutureTask requiere un objeto invocable, crearemos una implementación de llamada sencilla.

import java.util.concurrent.Callable; 

    public class MyCallable implements Callable<String> { 

     private long waitTime; 

     public MyCallable(int timeInMillis){ 
      this.waitTime=timeInMillis; 
     } 
     @Override 
     public String call() throws Exception { 
      Thread.sleep(waitTime); 
      //return the thread name executing this callable task 
      return Thread.currentThread().getName(); 
     } 

    } 

Aquí hay un ejemplo del método FutureTask y está mostrando los métodos más utilizados de FutureTask.

import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.FutureTask; 
import java.util.concurrent.TimeUnit; 
import java.util.concurrent.TimeoutException; 

public class FutureTaskExample { 

    public static void main(String[] args) { 
     MyCallable callable1 = new MyCallable(1000); 
     MyCallable callable2 = new MyCallable(2000); 

     FutureTask<String> futureTask1 = new FutureTask<String>(callable1); 
     FutureTask<String> futureTask2 = new FutureTask<String>(callable2); 

     ExecutorService executor = Executors.newFixedThreadPool(2); 
     executor.execute(futureTask1); 
     executor.execute(futureTask2); 

     while (true) { 
      try { 
       if(futureTask1.isDone() && futureTask2.isDone()){ 
        System.out.println("Done"); 
        //shut down executor service 
        executor.shutdown(); 
        return; 
       } 

       if(!futureTask1.isDone()){ 
       //wait indefinitely for future task to complete 
       System.out.println("FutureTask1 output="+futureTask1.get()); 
       } 

       System.out.println("Waiting for FutureTask2 to complete"); 
       String s = futureTask2.get(200L, TimeUnit.MILLISECONDS); 
       if(s !=null){ 
        System.out.println("FutureTask2 output="+s); 
       } 
      } catch (InterruptedException | ExecutionException e) { 
       e.printStackTrace(); 
      }catch(TimeoutException e){ 
       //do nothing 
      } 
     } 

    } 
} 
Cuestiones relacionadas