2010-12-03 20 views
23

Future.get (timeout) no arroja confiablemente TimeoutException después del tiempo de espera dado. ¿Es este comportamiento normal o puedo hacer algo para que esto sea más confiable? Esta prueba falla en mi máquina. Sin embargo, si duermo durante 3000 en lugar de 2000, pasará.¿Por qué Java Future.get (timeout) no es confiable?

public class FutureTimeoutTest { 
@Test 
public void test() throws 
    ExecutionException, 
    InterruptedException { 

    ExecutorService exec = Executors.newSingleThreadExecutor(); 
    final Callable call = new Callable() { 
     @Override 
     public Object call() throws Exception { 
      try { 
       Thread.sleep(2000); 
      } catch (InterruptedException ex) { 
       ex.printStackTrace(); 
      } 
      return 0; 
     } 
    }; 
    final Future future = exec.submit(call); 
    try { 
     future.get(1000, TimeUnit.MILLISECONDS); 
     fail("expected TimeoutException"); 
    } catch (TimeoutException ignore) { 
    } 
} 

}

+0

interesante. Hace ¿varía según la plataforma? Podría ser un error en la JVM o el sistema operativo. – JOTN

+0

No estoy seguro de que esto realmente cuente como un error, per se. Es probable que haya un cierto no determinismo en cuánto tiempo habrá pasado entre comenzando ese otro hilo e incluso comenzando a esperar que se complete el futuro. Sí, 1 segundo parece un poco demasiado, pero no veo nada en los documentos que en realidad ofrezca garantías duras sobre los plazos. –

+0

Solo lo he probado Windows XP. Estoy de acuerdo en que no puede haber determinismo, sin embargo no hay h sucediendo en este ejemplo. Mi caso en el mundo real tiene múltiples hilos y hay un bloqueo dentro de JNI en las respuestas de la red. Puedo entender alguna variabilidad en el tiempo de espera en ese caso pero aquí? –

Respuesta

15

No hay ninguna razón para esperar que la prueba pase. Dado que envía la tarea para su ejecución y espera a que se complete, puede pasar cualquier cantidad de tiempo antes de que comience la espera en Future#get(), lo que permite que la tarea tarde un montón en agotar la duración del sueño y finalizar.

En su caso, podemos suponer que el subproceso que se ejecuta dentro del Executor obtiene foco mientras que el subproceso principal que se ejecuta en test() está en espera, a pesar de estar en estado ejecutable. En cuanto a la diferencia observada entre el bloqueo de la tarea enviada durante dos y tres segundos, espero que puedas encontrar situaciones en las que incluso tres segundos sean insuficientes, dependiendo de lo que estén haciendo otros procesos en tu computadora.

11

@seh tiene razón.

Usted está contando con lo que comúnmente se denomina comportamiento "en tiempo real" de Java. Esto no se puede lograr de manera confiable a menos que use bibliotecas en tiempo real en una distribución Java capaz en tiempo real que se ejecute en un sistema operativo en tiempo real.

Sólo para ilustrar, la implementación de hilos de Java en las JVM modernas como HotSpot se basa en programador de subprocesos nativo del sistema operativo host para decidir qué temas se ejecute cuando. A menos que el programador de hilos esté específicamente al tanto de los plazos y cosas en tiempo real, es probable que tome una vista de "todo el sistema" al decidir qué hilos ejecutar. Si el sistema está cargado, es posible que ningún subproceso en particular se programe para ejecutarse durante segundos ... o más ... después de que hayan pasado las condiciones que impidieron su ejecución (por ejemplo, la espera de un evento del temporizador).

Luego está el problema de que el Java GC puede causar todos los otros hilos para bloquear.

Si realmente necesita el comportamiento en tiempo real a partir de Java, que está disponible. Por ejemplo:

Sin embargo, debe esperar cambiar sus aplicaciones para utilizar diferentes API para darle un comportamiento en tiempo real.

7

Tengo que decir, creo que los otros dos respuestas actualmente tienen una opinión innecesariamente baja concurrencia de las clases Java. No le darán una precisión de milisegundos (lo que esperan las aplicaciones "reales" en tiempo real), pero generalmente funcionan bastante bien. He escrito servicios comerciales a gran escala utilizando Futures and Executors y normalmente trabajaron en 10 milisegundos de los tiempos esperados, incluso bajo carga.

He corrido esta prueba, tanto en MacOS 10.6 con Java 1.6 y WinXP w/Java 1.6.0_22 y ambos funcionan como se esperaba.

he modificado el código de la siguiente manera para probar la exactitud:

long time1 = System.nanoTime(); 

    System.out.println("Submitting"); 
    final Future<Object> future = exec.submit(call); 
    try { 
     future.get(1000, TimeUnit.MILLISECONDS); 

     long time2 = System.nanoTime(); 
     System.out.println("No timeout after " + 
          (time2-time1)/1000000000.0 + " seconds"); 

     fail("expected TimeoutException"); 
    } catch (TimeoutException ignore) { 
     long time2 = System.nanoTime(); 
     System.out.println("Timed out after " + 
          (time2-time1)/1000000000.0 + " seconds"); 
    } 
    finally { 
     exec.shutdown(); 
    } 

En este XP imprime "cronometrados después de 1.002598934 segundos" y en lo imprime MacOS X 'cronometrados después 1.003158 segundos'.

Si el cartel original describiría su sistema operativo y la versión de JDK, tal vez podríamos determinar si se trata de un error en particular.

+0

intente ejecutar eso en un sistema cargado y ver si es tan preciso. En un buen día funciona, en un mal día no es así, así que no codifique sus aplicaciones (o pruebas unitarias) para * confiar * en él. –

+0

@ Stephen-C - Por supuesto, no dependería de obtener siempre tiempos perfectos en una aplicación de producción, pero en realidad he usado código como este para servicios de tiempo crítico en un dotcom muy grande y para al menos 99.9% de funcionó según lo requerido (teníamos un margen de seguridad ~ 50 ms). – andrewmu

+0

Lo probé en Mac OSX 10.6.5 con Java 1.6 y funciona. En la oficina estaba usando Windows XP Sun Java 1.6, no estoy seguro de la versión secundaria, pero está bastante actualizada. Eso estaba fallando el viernes. Me di cuenta de que mi código del mundo real tendría este tiempo de desconexión poco confiable la mayoría de los días, pero sí tuve un día en el que el tiempo de espera se produciría en unos pocos ms. del tiempo especificado en Future.get(). –

Cuestiones relacionadas