2008-12-23 16 views
13

Tengo un programa que continuamente sondea la base de datos para cambiar el valor de algún campo. Se ejecuta en segundo plano y actualmente utiliza un método while (true) y un método sleep() para establecer el intervalo. Me pregunto si esta es una buena práctica. Y, ¿cuál podría ser una forma más eficiente de implementar esto? El programa está destinado a ejecutarse en todo momento.Java while loop y Threads!

En consecuencia, la única manera de detener el programa es emitiendo un kill en el ID del proceso. El programa podría estar en el medio de una llamada JDBC. ¿Cómo podría terminar con más gracia? Entiendo que la mejor opción sería diseñar algún tipo de estrategia de salida mediante el uso de una bandera que será revisada periódicamente por el hilo. Pero, no puedo pensar en una forma/condición de cambiar el valor de esta bandera. ¿Algunas ideas?

Respuesta

12

Me pregunto si esta es una buena práctica?

No. No es bueno. A veces, es todo lo que tienes, pero no es bueno.

Y, ¿cuál podría ser una forma más eficiente de implementar esto?

¿Cómo entran las cosas en la base de datos en primer lugar?

El mejor cambio es arreglar los programas que insertan/actualizan la base de datos para realizar solicitudes que van a la base de datos y a su programa. Un tema JMS es bueno para este tipo de cosas.

El siguiente mejor cambio es agregar un desencadenador a la base de datos para poner en cola cada evento de inserción/actualización en una cola. La cola podría alimentar un tema JMS (o cola) para que lo procese su programa.

El plan alternativo es su circuito de votación.

Sin embargo, su ciclo de sondeo no debe funcionar de manera trivial. Debería dejar caer un mensaje en una cola para que funcione algún otro proceso JDBC. Una solicitud de finalización es otro mensaje que puede soltarse en la cola JMS. Cuando su programa recibe el mensaje de finalización, debe completarse con la solicitud previa de JDBC y puede detenerse correctamente.

Antes de hacer nada de esto, mire las soluciones de ESB. Sun JCAPS o TIBCO ya tienen esto. Un ESB de código abierto como Mulesource o Jitterbit ya puede tener esta funcionalidad ya construida y probada.

+0

+1 para una respuesta completa! –

8

Esto es realmente un problema demasiado grande para responder completamente en este formato. Hazte un favor e ir a comprar Java Concurrency in Practice. No hay mejor recurso para concurrencia en la plataforma Java 5+ que existe. Hay capítulos enteros dedicados a este tema.

En el asunto de matar su proceso durante una llamada JDBC, eso debería estar bien. Creo que hay problemas con la interrupción de una llamada JDBC (¿en que no se puede?) Pero ese es un problema diferente.

+1

+1 Si encuentras que el libro de Goetz es un poco abrumador (lo hice al principio, hay mucha información en él), te sugiero que leas la tercera edición de Java Threads para una introducción fácil con más código. – blank

+0

¿Hay un resumen para esta pregunta específica? ¿Me gusta "usar esta clase"? ¿O algo? – OscarRyz

2

Configure un controlador de señal para SIGTERM que establece un indicador que le indicará a su bucle que salga la próxima vez.

+0

"Configurar un manejador de señal para SIGTERM" ¿Cómo se hace esto? – OscarRyz

+0

google 'manejador de señal de Java'. – chaos

0

Si ese es su aplicación y se puede modificar, se puede:

  • hacer que lee un archivo
  • Leer el valor de una bandera.
  • Cuando quiera matarlo, simplemente modifique el archivo y la aplicación saldrá con gracia.

No es necesario que lo trabaje más que eso.

+0

Dado que se trata de un ciclo while con un intervalo de alrededor de 8 segundos, el IO involucrado en la lectura del archivo será bastante extenso. – Epitaph

3

Tenga en cuenta que un temporizador (o similar) estarían mejor en la que al menos podría volver a utilizarlo y dejar que haga con todos los detalles de dormir, programación, manejo de excepciones, etc ...

Hay muchas razones por las que tu aplicación podría morir. No te concentres solo en el uno.

Si teóricamente es posible que tu trabajo JDBC deje las cosas en un estado medio correcto, entonces tienes un error que debes corregir. Todo su trabajo DB debe estar en una transacción. Debería ir o no ir.

1

Acerca de la pregunta "El programa podría estar en el medio de una llamada JDBC. ¿Cómo podría terminar con más elegancia?" - ver How can I abort a running jdbc transaction? Tenga en cuenta que el uso de una encuesta con sleep() rara vez es la solución correcta. Implementado incorrectamente, puede terminar acaparando recursos de la CPU (el programador de hilos JVM termina gastando una cantidad excesiva de tiempo durmiendo y despertando hilo).

3

Esto es Java. Mueva su procesamiento a un segundo hilo. Ahora puede

  • Leído de stdin en un bucle. Si alguien escribe "QUIT", configure el indicador while en falso y salga.
  • Crea un marco AWT o Swing con un botón de DETENER.
  • Imagine que es un daemon de Unix y cree un socket de servidor. Espere a que alguien abra el socket y envíe "QUIT". (Esto tiene la ventaja añadida de que puede cambiar el modo de espera por uno con tiempo de espera).

Debe haber cientos de variantes en esto.

0

He creado una clase de servicio en la biblioteca de utilidades de mi empresa actual para este tipo de problemas:

public class Service implements Runnable { 

    private boolean shouldStop = false; 

    public synchronized stop() { 
     shouldStop = true; 
     notify(); 
    } 

    private synchronized shouldStop() { 
     return shouldStop; 
    } 

    public void run() { 
     setUp(); 
     while (!shouldStop()) { 
      doStuff(); 
      sleep(60 * 1000); 
     } 
    } 

    private synchronized sleep(long delay) { 
     try { 
      wait(delay); 
     } catch (InterruptedException ie1) { 
      /* ignore. */ 
     } 
    } 

} 

Por supuesto, esto está lejos de ser completa, sino que debe obtener la esencia. Esto le permitirá simplemente llamar al método stop() cuando desee que el programa se detenga y saldrá limpiamente.

+0

Pequeño comentario: maneje la InterruptedException - vea http://www-128.ibm.com/developerworks/java/library/j-jtp05236.html – noahlz

+0

¡Gracias por el enlace! ¡Excelente artículo! –

6

Como han dicho otros, el hecho de que usted tiene que sondear es probablemente indicativo de un problema más profundo con el diseño de su sistema ... pero a veces es así, así que ...

Si desea manejar "matar" el proceso un poco más gracia, usted puede instalar un gancho de cierre que se llama cuando se pulse Ctrl +C:

volatile boolean stop = false; 

Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") { 

    public void run() { 

     stop = true; 
    } 
}); 

luego periódicamente verifica la variable de parada.

Una solución más elegante es de esperar en un evento:

boolean stop = false; 

final Object event = new Object(); 

Runtime.getRuntime().addShutdownHook(new Thread("shutdown thread") { 

    public void run() { 

     synchronized(event) { 

      stop = true; 
      event.notifyAll(); 
     } 
    } 
}); 

// ... and in your polling loop ... 
synchronized(event) { 

    while(!stop) { 

     // ... do JDBC access ... 
     try { 

      // Wait 30 seconds, but break out as soon as the event is fired. 
      event.wait(30000); 
     } 

     catch(InterruptedException e) { 

      // Log a message and exit. Never ignore interrupted exception. 
      break; 
     } 
    } 
} 

O algo por el estilo.

0

Puede hacer que el campo sea un valor compuesto que incluye (conceptualmente) un ID de proceso y una marca de tiempo. [Mejor aún, use dos o más campos.] Comience un hilo en el proceso que posee acceso al campo, y póngalo en bucle, durmiendo y actualizando la marca de tiempo. Luego, un proceso de sondeo que espera tener acceso al campo puede observar que la marca de tiempo no se ha actualizado en algún tiempo T (que es mucho mayor que el intervalo de suspensión del ciclo de actualización) y supone que el proceso anterior ha desaparecido. .

Pero esto todavía es propenso a fallar.

En otros idiomas, siempre trato de usar las llamadas flock() para sincronizar en un archivo. No estoy seguro de cuál es el equivalente de Java. Obtenga simultaneidad real si es posible.

0

Me sorprende que nadie haya mencionado el mecanismo de interrupción implementado en Java. Se supone que es una solución al problema de detener un hilo. Todas las otras soluciones tienen al menos un defecto, por eso es necesario implementar este mecanismo en la biblioteca de concurrencia de Java.

Puede detener un hilo enviándole un mensaje de interrupción(), pero hay otras formas en que los hilos se interrumpen. Cuando esto sucede, se lanza una excepción interrumpida. Es por eso que debes manejarlo cuando llamas a sleep() por ejemplo. Ahí es donde puedes hacer la limpieza y finalizar con gracia, como cerrar la conexión de la base de datos.

0

Creo que debería sondearlo con timertask en su lugar.

Mi computadora ejecuta un ciclo while 1075566 veces en 10 segundos. Eso es 107557 veces en un segundo.

¿Con qué frecuencia es realmente necesario sondearlo? Un TimerTask funciona a su velocidad máxima de 1000 veces en 1 segundo. Le da un parámetro en int (milisegundos) como parámetros. Si está satisfecho con eso, eso significa que se esfuerza su CPU 108 veces menos con esa tarea.

Si está contento con el sondeo una vez por segundo que es (108 * 1000). 108 000 veces menos esfuerzo. Eso también significa que puede verificar 108 000 valores con la misma tensión de CPU que tenía con su ciclo while - porque no asigna su CPU para comprobar con tanta frecuencia. Recuerde que la CPU tiene un ciclo de reloj. El mío es 3 600 000 000 hercios (ciclos por segundo).

Si su objetivo es tenerlo actualizado para un usuario, puede ejecutar una comprobación cada vez que el usuario inicie sesión (o lo deje manualmente solicitar una actualización), que prácticamente no forzaría la CPU en absoluto.

También puede usar thread.sleep(miliseconds); para reducir la tensión de su hilo de sondeo (ya que no será el sondeo con la frecuencia) que está haciendo.

0

Java9 tiene otra respuesta "potencial" para esto: Thread.onSpinWait():

Indica que la persona que llama está momentáneamente incapaz de progresar, hasta la aparición de una o más acciones por parte de otras actividades. Al invocar este método dentro de cada iteración de una construcción de bucle spin-wait, el hilo de llamada indica al tiempo de ejecución que está ocupado, esperando. El tiempo de ejecución puede tomar medidas para mejorar el rendimiento de invocar construcciones de bucle spin-wait.

Ver JEP 285 para más detalles.