2008-09-18 12 views
28

Aquí está mi problema: tengo un diálogo con algunos parámetros que el usuario puede cambiar (a través de una ruleta, por ejemplo). Cada vez que se cambia uno de estos parámetros, inicio un hilo para actualizar una vista 3D de acuerdo con el nuevo valor del parámetro. Si el usuario cambia otro valor (o el mismo valor nuevamente haciendo clic muchas veces en la flecha giratoria) mientras el primer hilo está funcionando, me gustaría abortar el primer hilo (y la actualización de la vista 3D) y lanzar un nuevo uno con el último valor de parámetro.¿Cómo abortar un hilo de una manera rápida y limpia en Java?

¿Cómo puedo hacer algo como eso?

PD: No hay ningún bucle en el método run() de mi hilo, por lo que no es una opción verificar: un hilo que actualiza la vista 3D solo llama a un único método que es muy largo de ejecutar. No puedo agregar ningún indicador en este método que pida abortar ya que no tengo acceso a su código.

+0

Puede darnos alguna información más sobre el API que está utilizando? Dice que envía algo a algún método de una biblioteca de terceros, y esto lleva mucho tiempo. ¿Este método modifica directamente el objeto que pasa? O, ¿recibe una devolución de llamada con los resultados? –

+0

¿Alguna vez este subproceso finaliza normalmente antes de que se complete la aplicación? Si es así, ¿qué causa que se detenga? –

Respuesta

12

Pruebe la interrupción() como algunos han dicho para ver si hace alguna diferencia a su hilo.Si no, intente destruir o cerrar un recurso que detendrá el hilo. Eso tiene una posibilidad de ser un poco mejor que tratar de lanzar Thread.stop() a él.

Si el rendimiento es tolerable, puede ver cada actualización 3D como un evento discreto no interrumpible y simplemente dejar que se ejecute hasta la conclusión, verificando después si hay una nueva actualización más reciente para realizar. Esto podría hacer que la GUI sea un poco agitada para los usuarios, ya que podrían hacer cinco cambios, luego ver los resultados gráficos de cómo eran las cosas hace cinco cambios, y luego ver el resultado de su último cambio. Pero dependiendo de cuánto dura este proceso, podría ser tolerable y evitaría tener que matar el hilo. Diseño podría tener este aspecto:

boolean stopFlag = false; 
Object[] latestArgs = null; 

public void run() { 
    while (!stopFlag) { 
    if (latestArgs != null) { 
     Object[] args = latestArgs; 
     latestArgs = null; 
     perform3dUpdate(args); 
    } else { 
     Thread.sleep(500); 
    } 
    } 
} 

public void endThread() { 
    stopFlag = true; 
} 

public void updateSettings(Object[] args) { 
    latestArgs = args; 
} 
+0

Esa será mi solución, gracias. Usar una única secuencia de bucle en lugar de generar nuevas cada vez parece una buena opción. Solo hice una pequeña modificación: solo llamo perform3dUpdate() si se modificaron las últimas modificaciones desde la última pasada – jumar

+7

¡Me alegro de haber podido ayudar, jumar! Esta fue mi primera publicación Stackoverflow; Me inscribí específicamente para publicar esta idea. :) – skiphoppy

+0

Dos modificaciones que haría a endThread: 1. Si tiene acceso al objeto de la secuencia para la clase actual, entonces lo interrumpiría para abortar la llamada de sleep() temprano. 2. De nuevo, si puedes encontrar el hilo, haz un thread.join() al final de endThread() – nsayer

8

El hilo que está actualizando la vista 3D debería revisar periódicamente alguna bandera (use un volatile boolean) para ver si debería terminar. Cuando desee abortar el hilo, simplemente configure el indicador. Cuando el hilo vuelva a verificar el indicador, simplemente debería salir del bucle que esté utilizando para actualizar la vista y regresar desde su método run.

Si realmente no puede acceder al código que el hilo está ejecutando para que marque un indicador, entonces no hay una forma segura de detener el hilo. ¿Este subproceso termina alguna vez normalmente antes de que se complete su aplicación? Si es así, ¿qué causa que se detenga?

Si se ejecuta durante un período de tiempo prolongado y simplemente debe finalizarlo, puede considerar utilizar el método obsoleto Thread.stop(). Sin embargo, fue desaprobado por una buena razón. Si ese subproceso se detiene mientras está en medio de una operación que deja algo en un estado incoherente o algún recurso no se limpia correctamente, entonces podría estar en problemas. Aquí hay una nota del documentation:

Este método es inherentemente insegura. Detención de un hilo con Thread.stop hace que para desbloquear todos los monitores que se ha bloqueado (como una consecuencia natural de la sin control excepción ThreadDeath propagar hasta la pila). Si alguno de los objetos anteriormente protegidos por estos monitores se encontraban en un estado incoherente, los objetos dañados se vuelven visibles a otros hilos, lo que podría resultar en en comportamiento arbitrario. Muchos usos de la parada deben reemplazarse por el código que simplemente modifica alguna variable a indica que la secuencia de destino debe dejar de funcionar. El hilo de destino debe comprobar esta variable regularmente, y devolver de manera ordenada su método de ejecución si la variable indica que debe dejar de funcionar. Si el subproceso objetivo espera largos períodos (en una variable de condición, para el ejemplo ), el método de interrupción debe utilizarse para interrumpir la espera. Para más información, ver Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?

+0

La banda de rodadura que actualiza la vista 3D básicamente solo llama a un único método que es muy largo de ejecutar. No puedo agregar ninguna bandera en este método ya que no tengo acceso a su código. Entonces esta solución no se puede usar aquí. – jumar

+0

La solución propuesta por Dave L. es correcta. Si no tiene acceso a la fuente del código de terceros, tal vez el único recurso que le queda sea la manipulación de bytecode. Usando una biblioteca como ASM (http://asm.objectweb.org/) puede modificar una clase en tiempo de ejecución e "inyectar" su propio código. –

-1

Tal vez esto le puede ayudar: How can we kill a running thread in Java?

se puede matar a un hilo en particular mediante el establecimiento de una variable de clase externa.

Class Outer 
{  
    public static flag=true; 
    Outer() 
    { 
     new Test().start(); 
    } 
    class Test extends Thread 
    {    
     public void run() 
     { 
     while(Outer.flag) 
     { 
      //do your work here 
     } 
     } 
    } 
    } 

si desea detener el hilo de arriba, ponga variable de indicador a false. La otra forma de matar un hilo es simplemente registrarlo en ThreadGroup, luego llamar al destroy(). De esta forma también se puede usar para eliminar subprocesos similares al crearlos como grupo o registrarse con un grupo.

+4

Si desea que las personas lo tomen en serio, deje de usar "u" y "ur". Tómese el tiempo para escribir los 2 caracteres extra adicionales, no le demorará tanto. –

1

La forma en que han puesto en práctica algo como esto en el pasado es implementar un método en mi shutdown()Runnable subclase que establece una variable de instancia llamada should_shutdown true. El método run() normalmente hace algo en un bucle, y comprobará periódicamente should_shutdown y cuando sea verdadero, regresa o llama al do_shutdown() y luego regresa.

Debe tener a mano una referencia del hilo de trabajo actual, y cuando el usuario cambie un valor, llame al shutdown() en el hilo actual y espere a que se cierre. Entonces puedes lanzar un nuevo hilo.

yo no recomendaría el uso de Thread.stop ya que está desfasada y la última vez que lo comprobé.

Editar:

Leer su comentario acerca de cómo su subproceso de trabajo sólo llama a otro método que toma un tiempo para funcionar, lo que lo anterior no se aplica. En este caso, sus únicas opciones reales son intentar llamar al interrupt() y ver si tiene algún efecto. De lo contrario, considere de alguna manera que causa manualmente la función a la que llama su hilo de trabajo para interrumpir. Por ejemplo, parece que está haciendo una representación compleja, por lo que quizás destruya el lienzo y provoque una excepción. Esta no es una buena solución, pero hasta donde puedo decir, esta es la única manera de detener un hilo en juicios como este.

1

Un hilo se cerrará una vez que está run() método es completa, por lo que necesita algún tipo de control que hará que se termine el método.

Puede interrumpir el hilo, y luego tener algún control que verifique periódicamente isInterrupted() y vuelva al método run().

También podría usar un booleano que se comprueba periódicamente dentro de la secuencia, y lo devuelve en ese caso, o poner el hilo dentro de un bucle si realiza alguna tarea repetitiva y luego saldrá del método run() cuando establecer el booleano Por ejemplo,

static boolean shouldExit = false; 
Thread t = new Thread(new Runnable() { 
    public void run() { 
     while (!shouldExit) { 
      // do stuff 
     } 
    } 
}).start(); 
1

matar Desafortunadamente un hilo es inherentemente insegura debido a las posibilidades de utilización de los recursos que se pueden sincronizar mediante esclusas y si el hilo se mata actualmente tiene un bloqueo podría resultar en el programa de entrar en punto muerto (constante intentar agarrar un recurso que no se puede obtener). Deberá verificar manualmente si necesita eliminarse del hilo que desea detener. Volatile asegurará la verificación del verdadero valor de la variable en lugar de algo que pueda haberse almacenado previamente. En una nota al margen, Thread.join en el hilo de salida para asegurarse de que espere hasta que el hilo que se está muriendo se haya ido antes de hacer algo en lugar de verificar todo el tiempo.

4

En lugar de rodar su propia bandera booleana, ¿por qué no utilizar el mecanismo de interrupción hilo ya en hilos de Java? Dependiendo de cómo se implementaron las partes internas en el código que no puede cambiar, es posible que también pueda cancelar parte de su ejecución.

Rosca exterior:

if(oldThread.isRunning()) 
{ 
    oldThread.interrupt(); 
    // Be careful if you're doing this in response to a user 
    // action on the Event Thread 
    // Blocking the Event Dispatch Thread in Java is BAD BAD BAD 
    oldThread.join(); 
} 

oldThread = new Thread(someRunnable); 
oldThread.start(); 

interior Ejecutable/Tema:

public void run() 
{ 
    // If this is all you're doing, interrupts and boolean flags may not work 
    callExternalMethod(args); 
} 

public void run() 
{ 
    while(!Thread.currentThread().isInterrupted) 
    { 
     // If you have multiple steps in here, check interrupted peridically and 
     // abort the while loop cleanly 
    } 
} 
+0

Para responder a su pregunta, es porque Thread.intrerruption() puede causar efectos secundarios lanzando excepciones durante ciertas operaciones de IO o concurrencia. Algunas veces esto es exactamente lo que quieres, pero no siempre. –

0

Puesto que usted está tratando con código que no tiene acceso a usted está probablemente fuera de suerte. El procedimiento estándar (como se describe en las otras respuestas) es tener un indicador que se verifica periódicamente por el hilo en ejecución. Si la bandera está configurada, haga la limpieza y salga.

Como esa opción no está disponible para usted, la única otra opción es forzar el cierre del proceso en ejecución. Esto solía ser posible llamando Thread.stop(), pero ese método ya no se utiliza de forma permanente por la siguiente razón (copiado de los javadocs):

Este método es inherentemente insegura. Detener un hilo con Thread.stop hace que desbloquee todos los monitores que ha bloqueado (como consecuencia natural de la excepción ThreadDeath no verificada que se propaga por la pila). Si alguno de los objetos previamente protegidos por estos monitores se encontraba en un estado incoherente, los objetos dañados se vuelven visibles para otros hilos, lo que puede generar un comportamiento arbitrario.

Más información sobre este tema puede encontrarse en here.

Una forma absolutamente segura de poder realizar su pedido (aunque esta no es una forma muy eficiente de hacerlo) es iniciar un nuevo proceso java a través del Runtime.exec() y luego detener ese proceso según sea necesario a través del Process.destroy(). Sin embargo, compartir el estado entre procesos como este no es exactamente trivial.

0

En lugar de jugar con el inicio y la detención de un hilo, ¿ha considerado que el hilo observe las propiedades que está cambiando a través de su interfaz? En algún momento, todavía querrás una condición de detención para tu hilo, pero esto puede hacerse así. Si eres fanático de MVC, esto encaja bien en ese tipo de diseño

Lo sentimos, después de volver a leer tu pregunta, ni esta ni ninguna de las otras sugerencias de 'variable de verificación' resolverán tu problema.

3

¿No es esto un poco como preguntar "¿Cómo puedo abortar un hilo cuando no hay otro método que no sea Thread.stop() disponible?"

Obviamente, la única respuesta válida es Thread.stop(). Es feo, podría romper las cosas en algunas circunstancias, puede conducir a fugas de memoria/recursos, y es mal visto por TLEJD (La Liga de Desarrolladores de Java Extraordinarios), sin embargo, todavía puede ser útil en algunos casos como este. Realmente no hay otro método si el código de terceros no tiene algún método cercano disponible.

OTOH, a veces hay métodos de cierre de puerta trasera. Es decir, cerrar un flujo subyacente con el que está trabajando o algún otro recurso que necesita para hacer su trabajo. Esto rara vez es mejor que simplemente llamar a Thread.stop() y dejar que experimente una excepción ThreadDeathException.

1

Parece que no tiene ningún control sobre el hilo que está renderizando la pantalla, pero parece que tiene el control del componente del control giratorio. Deshabilitaría la ruleta mientras el hilo está renderizando la pantalla. De esta forma, el usuario al menos tiene algunos comentarios relacionados con sus acciones.

1

sugiero que acaba de evitar múltiples hilos mediante el uso de espera y notifica de manera que si el usuario cambia el valor muchas veces sólo se ejecutará una vez al hilo. Si los usuarios cambian el valor 10 veces, se disparará desde el subproceso en el primer cambio y luego todos los cambios realizados antes de que se complete el subproceso se "envían" en una sola notificación. Eso no detendrá un hilo, pero no hay buenas maneras de hacerlo en base a su descripción.

1

Las soluciones que tienen como objetivo el uso de un campo booleano son la dirección correcta. Pero el campo debe ser volátil. El lenguaje Java Spec says:

"Por ejemplo, en el siguiente fragmento de código (roto), se supone que this.done es un volátil campo no boolean:

while (!this.done) 
    Thread.sleep(1000); 

el compilador está libre de leer el campo this.done una sola vez, y reutilizar el valor en caché en cada ejecución del ciclo. Esto significaría que el ciclo nunca terminaría, incluso si otro hilo cambió el valor de this.done ".

Por lo que yo recuerdo "Java Concurrency in Pratice" propósitos de utilizar los interrupt() y interrupted() métodos de java.lang.Thread.

1

La respuesta aceptada a esta pregunta le permite enviar trabajos por lotes a un hilo de fondo. Esto podría ser un modelo mejor para eso:

public abstract class dispatcher<T> extends Thread { 

    protected abstract void processItem(T work); 

    private List<T> workItems = new ArrayList<T>(); 
    private boolean stopping = false; 
    public void submit(T work) { 
    synchronized(workItems) { 
     workItems.add(work); 
     workItems.notify(); 
    } 
    } 
    public void exit() { 
    stopping = true; 
    synchronized(workItems) { 
     workItems.notifyAll(); 
    } 
    this.join(); 
    } 
    public void run() { 
    while(!stopping) { 
     T work; 
     synchronized(workItems) { 
     if (workItems.empty()) { 
      workItems.wait(); 
      continue; 
     } 
     work = workItems.remove(0); 
     } 
     this.processItem(work); 
    } 
    } 
} 

Para utilizar esta clase, que se aplicará, proporcionando un tipo de T y una implementación de ProcessItem(). Luego solo construye uno y llama a start() en él.

Se podría considerar la adición de un método abortPending:

public void abortPending() { 
    synchronized(workItems) { 
    workItems.clear(); 
    } 
} 

para aquellos casos en que el usuario ha saltado por delante del motor de renderizado y quiere tirar el trabajo que se ha programado hasta el momento.

0

La respuesta correcta es no utilizar un hilo.

usted debe utilizar los ejecutores, ve el paquete: java.util.concurrent

+5

Lo siento pero dice que los ejecutores son mejores que los hilos sin explicar por qué es esto, no es útil. Así que les pregunto esto: ¿de qué manera los ejecutores son mejores que los hilos? ¿Son * siempre * mejores o hay veces en que los hilos son preferidos? –

Cuestiones relacionadas