util newscheduledthreadpool isterminated invokeall ejemplo concurrent java executor

newscheduledthreadpool - java threadpoolexecutor



Mejores prácticas de Java Executor para tareas que deberían ejecutarse para siempre (4)

¿Has probado Quartz framework ?

Estoy trabajando en un proyecto Java donde necesito tener varias tareas ejecutándose de forma asíncrona. Me hacen creer que el ejecutor es la mejor manera de hacerlo, así que me estoy familiarizando con eso. (¡Sí, se les paga para aprender!) Sin embargo, no está claro para mí cuál es la mejor manera de lograr lo que intento hacer.

En aras de la discusión, digamos que tengo dos tareas en ejecución. No se espera que ninguno de los dos finalice y ambos deberían ejecutarse durante la vida de la aplicación. Intento escribir una clase contenedora principal de tal manera que:

  • Si cualquiera de las tareas arroja una excepción, el contenedor la detectará y reiniciará la tarea.
  • Si cualquiera de las tareas se ejecuta hasta su finalización, el contenedor notará y reiniciará la tarea.

Ahora, debe notarse que la implementación para ambas tareas envolverá el código en run() en un bucle infinito que nunca se ejecutará hasta su finalización, con un bloque try / catch que debe manejar todas las excepciones de tiempo de ejecución sin interrumpir el bucle. Estoy tratando de agregar otra capa de certeza; si yo o alguien que me sigue hace algo estúpido que derrota estas salvaguardas y se detiene la tarea, la aplicación debe reaccionar de manera adecuada.

¿Hay alguna mejor práctica para abordar este problema que la gente más experimentada que yo recomendaría?

FWIW, he batido esta clase de prueba:

public class ExecTest { private static ExecutorService executor = null; private static Future results1 = null; private static Future results2 = null; public static void main(String[] args) { executor = Executors.newFixedThreadPool(2); while(true) { try { checkTasks(); Thread.sleep(1000); } catch (Exception e) { System.err.println("Caught exception: " + e.getMessage()); } } } private static void checkTasks() throws Exception{ if (results1 == null || results1.isDone() || results1.isCancelled()) { results1 = executor.submit(new Test1()); } if (results2 == null || results2.isDone() || results2.isCancelled()) { results2 = executor.submit(new Test2()); } } } class Test1 implements Runnable { public void run() { while(true) { System.out.println("I''m test class 1"); try {Thread.sleep(1000);} catch (Exception e) {} } } } class Test2 implements Runnable { public void run() { while(true) { System.out.println("I''m test class 2"); try {Thread.sleep(1000);} catch (Exception e) {} } } }

Se está comportando de la manera que yo quiero, pero no sé si hay problemas, ineficiencias o una total maldad esperando a sorprenderme. (De hecho, dado que soy nuevo en esto, me sorprendería si no hubiera algo mal / desaconsejado al respecto).

Cualquier idea es bienvenida.


Además de analizarlo, generalmente ejecuto código Java contra herramientas de análisis estático como PMD y FindBugs para buscar problemas más profundos.

Específicamente para este código, a FindBugs no le gustó que los resultados1 y los resultados2 no son volátiles en el inicio diferido, y que los métodos run () podrían ignorar la Excepción porque no se manejan explícitamente.

En general, estoy un poco receloso del uso de Thread.sleep para las pruebas de concurrencia, prefiriendo temporizadores o estados / condiciones de terminación. Invocar puede ser útil para devolver algo en caso de una interrupción que arroje una excepción si no se puede calcular un resultado.

Para obtener algunas mejores prácticas y más ideas para reflexionar, consulte Concurrencia en la práctica .


Me enfrenté a una situación similar en mi proyecto anterior, y luego de que mi código reventara a un cliente enojado, mis amigos y yo agregamos dos grandes protecciones:

  1. En el bucle infinito, también capturas los errores, no solo las excepciones. A veces suceden cosas no contempladas y Java le lanza un Error, no una Excepción.
  2. Use un interruptor de retroceso, de modo que si algo sale mal y no se puede recuperar, no intensifique la situación iniciando ansiosamente otro ciclo. En cambio, debe esperar hasta que la situación vuelva a la normalidad y luego comenzar de nuevo.

Por ejemplo, tuvimos una situación en la que la base de datos bajó y durante el ciclo se lanzó una SQLException. El desafortunado resultado fue que el código pasó nuevamente por el ciclo, solo para volver a la misma excepción, y así sucesivamente. Los registros mostraron que alcanzamos la misma SQLException unas 300 veces en un segundo. ... esto sucedió intermitentemente varias veces con pausas JVM ocasionales de 5 segundos más o menos, durante las cuales la aplicación no respondió, hasta que finalmente se arrojó un error y el hilo murió.

Así que implementamos una estrategia de reducción, que se muestra aproximadamente en el siguiente código, que si la excepción no es recuperable (o se espera que se recupere en cuestión de minutos), esperamos un tiempo más prolongado antes de reanudar las operaciones.

class Test1 implements Runnable { public void run() { boolean backoff = false; while(true) { if (backoff) { Thread.sleep (TIME_FOR_LONGER_BREAK); backoff = false; } System.out.println("I''m test class 1"); try { // do important stuff here, use database and other critical resources } catch (SqlException se) { // code to delay the next loop backoff = true; } catch (Exception e) { } catch (Throwable t) { } } } }

Si implementa sus tareas de esta manera, entonces no veo el punto de tener un tercer hilo "perro guardián" con el método checkTasks (). Además, por las mismas razones que expliqué anteriormente, sería cauteloso para comenzar nuevamente la tarea con el ejecutor. En primer lugar, debe comprender por qué falló la tarea y si el entorno se encuentra en una condición estable que la ejecución de la tarea nuevamente sería útil.


Qué tal esto

Runnable task = () -> { try{ // do the task steps here } catch (Exception e){ Thread.sleep (TIME_FOR_LONGER_BREAK); } }; ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate(task,0, 0,TimeUnit.SECONDS);