2011-12-27 22 views
6

Estoy revisando el código Java que, en esencia, es un proceso recurrente que mueve/lee/analiza algunos archivos de forma regular y genera datos en la base de datos. La parte que se repite se hace (más o menos) de la siguiente manera:Java - Thread.sleep en el método principal

public class CollectorMain { 
    public static boolean signalRecieved = false; 
    public static void main(String[] args) { 
     Runtime.getRuntime().addShutdownHook(new Thread() { 
       public void run() { 
       shutdown(); 
     }}); 
     while(!signalRecieved) { 
       Collector.execute(); 
       try { 
        Thread.sleep(60 * 1000); 
       } catch (InterruptedException e) { 
        break; 
       } 
     } 
     // some shutdown logic 
    } 
    public static void shutdown() { 
     signalReceived = true; 
    } 
} 

public class Collector() { 
    public static void execute() { 
     // Move files from the queue dir to temp location 
     // Read, parse files and insert records into database. 
     // Then delete the processed files 
    } 
} 

Mi recomendación fue refactorizar código para

  1. Crear instancia de colector y refactorizar método estático ejecutar() para el método de instancia
  2. Para use Runnable o TimerTask para manejar las invocaciones

Mi argumento fue que usar Thread.wait desde el método principal y combinarlo con acceso estático no es una buena forma de o f manejando un proceso repetible, especialmente haciendo el archivo IO. Para que el autor respondió (donde se cita)

La descripción de Ejecutable dice "debe aplicarse a cualquier clase cuyas instancias están destinados a ser ejecutado por un hilo". De hecho, I estoy evitando intencionalmente los hilos en este programa, por razones de costo requisitos de rendimiento.

Aquí hay otra cita del mismo debate que se espera ayude a aclarar la posición del autor

Técnicamente, Java no se ejecuta en absoluto, que es interpretado por la JVM que luego ejecuta las instrucciones de la máquina, para simular que el código Java se está ejecutando. Entonces, realmente es la JVM la que se ejecuta en un subproceso o varios subprocesos.

Pero como escritor de código Java, no me importa. Si no creo "hilos" en Java, el trabajo de la JVM es que parezca que no hay hilos , incluso si la JVM está utilizando hilos "debajo de las cubiertas".

Una pausa de Java no se ejecuta, se simula mediante una secuencia de instrucciones de la máquina que pueden o no llamar a un OS 'espera'. (Probablemente lo haga, porque la JVM no querría girar, grabar ciclos de CPU, pero eso es una opción de implementación de JVM).

así que tengo 2 preguntas:

  1. está poniendo Thread.wait en el método principal forma de fiar, segura y conveniente de hacer la tarea repetible en este caso? Y si no, ¿por qué no, ya que solo hay un hilo (principal) de ejecución?
  2. ¿Cuáles son las dificultades del uso de métodos estáticos en este contexto (si corresponde)?

Estaremos encantados de proporcionar información adicional si tiene alguna otra pregunta.

+0

¿No debería estar protegido 'signalRecibved' (por ejemplo' volátil') ya que se comparte entre el hilo principal y el gancho de cierre? – dacwe

+0

Buen punto, va a la idea de "no usar múltiples hilos" – Bostone

+0

Los ganchos de cierre __son hilos separados. – toto2

Respuesta

6

Realmente estás discutiendo sobre las decisiones de diseño y no decisiones de rendimiento.

La afirmación de su colega sobre cómo se implementa Thread.sleep es básicamente incorrecta por lo que puedo ver. En una JVM sensible en un sistema operativo sensible, Thread.sleep() se implementa utilizando un método nativo O/S para estacionar un hilo (o ponerlo en un estado de "espera temporizada" o lo que sea que quiera llamarlo en su sistema operativo) . O dicho de otra manera, mientras un hilo está durmiendo, consume cero CPU. Y en cualquier caso, TimerTask usará Thread.sleep (o similar-- No me acuerdo si usa el método park() introducido en Java 5, pero esencialmente no hace diferencia).

La JVM generalmente no tomará decisiones secretas sobre el roscado. Si solicita otro hilo, obtendrá uno; si no lo haces, no lo harás. Se crearán algunos hilos de "limpieza" para la recolección de basura, etc., pero en lo que se refiere a su código, puede suponer que no está ocurriendo ninguna secreta creación de hilos inteligentes.

Así que, volviendo a sus preguntas:

  • que duermen en el hilo principal es perfectamente seguro per se; obviamente, mientras el hilo principal está durmiendo, no ejecutará nada, pero si no lo necesitas, está bien;
  • si utiliza Thread.sleep() directamente en su código o usa uno de los métodos de utilidad "temporizador" es una decisión de diseño basada en si desea usar una biblioteca general que elimine detalles de implementación de su código, o si usted prefiere tener esos detalles presentes y bajo su control; en lo que respecta al rendimiento, tendrá pocas probabilidades si implementa las cosas correctamente;
  • si tiene un método estático o si una instancia de Collector no le importa, es solo una decisión de diseño que debe tomar en función de lo que parece más intuitivo: ¿ve Collector como una clase de "utilidad general"? ¿o como una "cosa" con estado que solicite para realizar operaciones?
+0

Me gusta esta respuesta. Esperaré a que la discusión continúe un poco, pero por ahora la tuya es mi respuesta favorita – Bostone

0

En el ejemplo de código publicado, me gustaría utilizar en lugar de Object.wait(60 * 1000)Thread.sleep(60 * 1000), a continuación, dentro del método shutdown, añadir una llamada a notifyAll() después de establecer signalReceived = true.Esto requerirá agregar los bloques de sincronización necesarios alrededor de los métodos de notificación y de espera. Como mínimo, esto proporcionará el beneficio de que el "ciclo de espera" pueda salir inmediatamente, en lugar de esperar a que transcurra primero el tiempo de espera. (Véase la respuesta de JB Nizet para algunos detalles adicionales en torno a este.)

Desde una perspectiva general - que tendría el colector implementar TimerTask (sub-interfaz de Runnable), programo con un Timer, y hacerse con él.

Algunos usuarios abogarán por el uso de la estática por motivos de rendimiento, que yo diría que son en su mayoría históricos. Mantener todo no estático permitirá el uso futuro de múltiples instancias dentro de la misma JVM, además de poder utilizar subclases para anular métodos y personalizar la implementación base.

+1

No hay método Thread.wait. –

+0

@JBNizet - Solucionado. Estás en lo cierto, 'wait' se declara en' Object', pero técnicamente, Thread también lo tiene a través de la herencia de 'Object'. :-) – ziesemer

+0

Este diseño hace que el apagado se ejecute en paralelo al colector. El código original se asegura de que el cierre se realice en el mismo hilo que el recopilador, lo cual es probablemente importante (si, por ejemplo, el cierre consiste en cerrar un socket o una conexión que el recopilador está utilizando). Para mí, si no se necesitan varios hilos, la solución original está bien. –

3

Estás confundiendo sleep y wait. sleep hace que el hilo actual permanezca inactivo durante un período de tiempo, y luego el hilo se reinicia por sí mismo. wait es un método de Object. Se usa para poner un hilo en estado de espera y solo saldrá de este estado si otro hilo lo despierta usando notify o notifyAll en el mismo objeto. Usar wait si solo hay un hilo ejecutándose hará que su programa cuelgue eternamente.

Leer http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html

+0

El uso del método 'wait' reemplazado que acepta un tiempo de espera puede usarse como una alternativa a' sleep', lo que le permite regresar mediante una llamada a 'notify' (' All'), o una vez que transcurra el tiempo de espera. – ziesemer

+1

De todos modos, me parece que el enganche de cierre no sirve para nada, ya que no cierra por sí solo, sino que solo le pide al hilo principal que lo haga cambiando una variable booleana. La JVM se detendrá tan pronto como haya finalizado la última secuencia de enlace de cierre. Por lo tanto, podría detenerse antes o en el medio de la lógica de apagado. Si el gancho de apagado no es útil, solo queda un hilo, y esperar no tiene sentido en este caso. –

1

Creo que su colega realmente no entiende la JVM y su modelo de subprocesamiento; su código no será mágicamente de múltiples hilos a menos que explícitamente lo haga de esa manera.Además, creo que los dos están trabajando demasiado duro en esto. Prueba la biblioteca de Quartz: http://quartz-scheduler.org/documentation/quartz-2.1.x/quick-start

Mi razonamiento es que Java Threading es difícil de acertar, especialmente cuando te encuentras haciendo esperar/notificar por tu cuenta y lidiando con todos los casos extremos. La biblioteca de cuarzo ha abstraído todo esto y ha puesto el aspecto repetitivo detrás del familiar patrón CRON.

Evitaría totalmente TimerTask, ya que tiene la mala costumbre de fallar silenciosamente si hay excepciones no controladas que se producen durante su ejecución.

llamar a métodos estáticos o no es nada del otro mundo, independientemente de su solución, la clave es entender qué estado se comparte a través de las discusiones y, o bien eliminar el compartir o sincronizar el acceso a ella para todos los Hilos obtener una visión coherente de los datos. Si yo estuviera en tu posición, le daría una oportunidad a Quartz. Si no quiere agregar otra biblioteca, JDK 1.5 (creo) trajo el ScheduledExectutorService que creo que realiza tareas repetitivas.

No importa qué camino tome, no escriba usted mismo el marco de programación/ejecución. Este es un problema resuelto en Quartz o ScheduledExectutorService.

+0

¿Por qué crees que es necesario enhebrar aquí? – jdigital

+0

Quartz estaba en mi lista de soluciones recomendadas – Bostone

+0

Ejecutable en Java es una abstracción de una tarea que se ejecutará en un subproceso. Cada programa que se ejecuta en Java lo hace en un subproceso, ya sea el subproceso principal o algún otro subproceso. El desafío que el tipo de DroidIn.net nos dio sobre cómo manejar mejor una tarea recurrente en su sistema. Esto me hizo pensar en Runnable y su relación con Threads. No es necesario enhebrar, pero este problema se tiene bastante en cuenta con los mecanismos Threading de JDK, sus extensiones como ScheduledExectutorService o una biblioteca como Quartz. No escriba este código usted mismo. –

Cuestiones relacionadas