¿Cómo se mata un hilo en Java?


374

¿Cómo matas a un java.lang.Threaden Java?


2
hasta ahora no puedes matar un hilo; porque destroy () nunca se implementa debido a la propensión al bloqueo muerto
AZ_

1
Prefiero la respuesta con respecto ExecutorStatusa esta pregunta: stackoverflow.com/questions/2275443/how-to-timeout-a-thread
Kirby

99
@loungerdork "Creo que Java debería implementar un método seguro de detención / destrucción para hilos desbocados sobre los que no tiene control, a pesar de las advertencias de perder bloqueos y otras trampas". Por lo tanto, desea una parada de hilos insegura . Creo que ya tienes uno.
DJClayworth

44
Es sorprendente qué tipo de preguntas obtendrían 212 votos a favor en 2009. Esto se destruiría de inmediato hoy.
Jonathon Reinhart

77
@JonathonReinhart: ¿Por qué es eso? Parece ser una pregunta legítima, incluso en estos días. ¿Quizás no conoces la frustración que causa cuando tienes hilos desbocados y solo puedes usar funciones obsoletas para manejar esto de alguna manera?
TFuto

Respuestas:


189

Vea este hilo de Sun sobre por qué se desaprobaronThread.stop() . Entra en detalles sobre por qué este era un mal método y qué se debe hacer para detener los subprocesos de forma segura en general.

La forma en que recomiendan es usar una variable compartida como un indicador que le pide al hilo de fondo que se detenga. Esta variable puede ser establecida por un objeto diferente que solicita la terminación del hilo.


1
si marca el hilo que ha interrumpido esAlive (), le devolverá verdadero y continuarán agregando a su ThreadGroup actual [], puede ver esto usando Thread.currentThread.getThreadGroup (). list (); imprimirá todos los hilos que tenga y verá varias instancias de su hilo si repite su flujo.
AZ_

3
Si está en una PC, entonces no hay problema, pero si está desarrollando un software para dispositivos móviles (Android lo he experimentado), obtendrá OutOfMemoryError
AZ_

2
Se podría / se debería tener en cuenta que, para garantizar una comunicación rápida de la solicitud de detención a través del indicador, la variable debe ser volátil (o el acceso a la variable debe estar sincronizado), como se indica en la recomendación.
mtsz

2
Ese enlace ha sido eliminado en este punto. Sin embargo, pude encontrarlo en archive.org: web.archive.org/web/20090202093154/http://java.sun.com/j2se/…
Jay Taylor

2
Yo uso el método getConnection()de java.sql.DriverManager. Si el intento de conexión tarda demasiado, trato de eliminar el hilo correspondiente llamando Thread.interrupt()pero no influye en el hilo en absoluto. Sin Thread.stop()embargo, funciona, aunque Oracle dice que no debería funcionar si interrupt()no funciona. Me pregunto cómo hacerlo funcionar y evitar el uso de métodos obsoletos.
Danny Lo

129

Generalmente no lo haces ..

Le pide que interrumpa lo que sea que esté haciendo usando Thread.interrupt () (enlace javadoc)

Una buena explicación de por qué está en el javadoc aquí (enlace de nota técnica de Java)


@Fredrik ¿Qué le sucede al contexto de Thread cuando interrupt()se llama al método? La pregunta principal está relacionada con la generación de registros para cada nuevo hilo.
ABcDexter

3
@ABcDexter El punto es que la interrupción no interrumpe nada, solo le indica al código en el hilo (o al código que llama el hilo) que alguien le ha pedido que interrumpa lo que sea que esté haciendo. Se supone que el subproceso detiene bien el procesamiento y el retorno, al igual que si se hiciera haciendo lo que debería (y en ese punto, el contexto del subproceso probablemente también se descarte). OTOH, si realmente hubiera detenido el hilo, su pregunta sería realmente buena y la respuesta no estaría definida.
Fredrik

64

En Java, los subprocesos no se eliminan, pero la detención de un subproceso se realiza de forma cooperativa . Se le pide al hilo que termine y el hilo puede cerrarse con gracia.

A menudo volatile booleanse usa un campo que el subproceso comprueba y termina periódicamente cuando se establece en el valor correspondiente.

Yo no me utilizar una booleanpara comprobar si el hilo debe terminar . Si lo usa volatilecomo un modificador de campo, esto funcionará de manera confiable, pero si su código se vuelve más complejo, ya que en su lugar usa otros métodos de bloqueo dentro del whileciclo, podría suceder que su código no termine en absoluto o al menos tarde más tiempo . podria querer.

Ciertos métodos de biblioteca de bloqueo admiten interrupción.

Cada hilo ya tiene un estado de interrupción de bandera booleana y debe usarlo. Se puede implementar así:

public void run() {
   try {
      while (!interrupted()) {
         // ...
      }
   } catch (InterruptedException consumed)
      /* Allow thread to exit */
   }
}

public void cancel() { interrupt(); }

Código fuente adaptado de Java Concurrency in Practice . Como el cancel()método es público, puede dejar que otro hilo invoque este método como lo desee.


¿Y qué hacer si ejecuta código no confiable como un complemento o script? Java ha incorporado un sandbox para código no confiable. Y esa caja de arena es inútil, permite trabajar sin una parada forzada. Imagina que estás escribiendo un navegador en Java. La capacidad de matar guiones de páginas arbitrarias no tiene precio.
ayvango

@ayvango Entonces tienes que ejecutar ese script en tu propia caja de arena. El entorno limitado de Java protege la máquina de la aplicación, no partes de la aplicación entre sí.
David Schwartz

@DavidSchwartz, ¿quiere decir que debería usar otra plataforma que no sea Java?
ayvango

@ayvango Todavía puede usar el entorno limitado de Java para proteger la máquina de su aplicación. Pero si desea proteger partes de su aplicación de otras partes de su aplicación, deberá elegir alguna herramienta que pueda hacer eso.
David Schwartz

@DavidSchwartz ASFAIK tales herramientas no podrían existir si no es compatible a nivel de plataforma. Pero, por supuesto, la tarea podría resolverse con un motor de script totalmente interpretado. Podría contar reducciones como lo hace erlang y hacer otras cosas.
ayvango

20

Una forma es establecer una variable de clase y usarla como centinela.

Class Outer {
    public static volatile flag = true;

    Outer() {
        new Test().start();
    }
    class Test extends Thread {

        public void run() {
            while (Outer.flag) {
                //do stuff here
            }
        }
    }

}

Establezca una variable de clase externa, es decir, flag = true en el ejemplo anterior. Póngalo en falso para 'matar' el hilo.


2
Solo como una sugerencia lateral: una variable como bandera solo funciona cuando el hilo se ejecuta y no está atascado. Thread.interrupt () debería liberar el hilo de la mayoría de las condiciones de espera (espera, suspensión, lectura de red, etc.). Por lo tanto, nunca debe atrapar la InterruptedException para que esto funcione.
ReneS el

12
Esto no es confiable; haga "bandera" volatilepara asegurarse de que funciona correctamente en todas partes. La clase interna no es estática, por lo que el indicador debería ser una variable de instancia. El indicador debe borrarse en un método de acceso para que se puedan realizar otras operaciones (como la interrupción). El nombre "bandera" no es descriptivo.
erickson

2
No entiendo el "tiempo" en el método de ejecución. ¿No significa esto que todo lo que está escrito en el método de ejecución se repetirá? esto no es algo que quisiéramos que hiciera el hilo en primer lugar :(

2
+1 haciendo mientras se prefiere (! Thread.currentThread (). IsInteruppted ())
Toby

2
Ambos casos fallan, por ejemplo, cuando abre un proceso externo dentro de while {// open ext process} y ese proceso se cuelga, ahora ni el hilo se interrumpirá ni llegará al final para verificar su condición booleana, y estará dejó colgado ... pruébelo, por ejemplo, inicie una consola de Python usando java.exec e intente recuperar el control sin escribir exit, y vea si hay una manera de matar ese proceso y salir ... no hay forma de salir de tal situación ...
Space Rocker

11

Hay una manera de cómo puedes hacerlo. Pero si tuvieras que usarlo, o eres un mal programador o estás usando un código escrito por malos programadores. Por lo tanto, debe pensar en dejar de ser un mal programador o dejar de usar este código incorrecto. Esta solución es solo para situaciones en las que NO HAY OTRA MANERA.

Thread f = <A thread to be stopped>
Method m = Thread.class.getDeclaredMethod( "stop0" , new Class[]{Object.class} );
m.setAccessible( true );
m.invoke( f , new ThreadDeath() );

2
No hay razones para hacer esto, porque todavía es posible llamar al público Thread.stopincluso si está en desuso.
Lii

1
@Lii Thread.stophace lo mismo pero también verifica el acceso y los permisos. El uso Thread.stopes bastante obvio, y no recuerdo la razón por la que lo usé en Thread.stop0lugar de eso. Quizás Thread.stopno funcionó para mi caso especial (Weblogic en Java 6). O tal vez porque Thread.stopestá en desuso y causa advertencia.
VadimPlatonov

1
En mi caso, esta era la única forma de detener un hilo interminable. Por alguna razón .stop () no detuvo el hilo, pero stop0 () sí
fiffy

10

Quiero agregar varias observaciones, basadas en los comentarios que se han acumulado.

  1. Thread.stop() detendrá un hilo si el administrador de seguridad lo permite.
  2. Thread.stop()es peligroso. Dicho esto, si está trabajando en un entorno JEE y no tiene control sobre el código que se llama, puede ser necesario; ver ¿Por qué Thread.stop está en desuso?
  3. Nunca debe dejar de detener un subproceso de trabajo contenedor. Si desea ejecutar un código que tiende a bloquearse, (con cuidado) inicie un nuevo hilo de demonio y vigílelo, eliminándolo si es necesario.
  4. stop()crea un nuevo ThreadDeathErrorerror en el hilo de llamada y luego arroja ese error en el hilo de destino . Por lo tanto, el seguimiento de la pila generalmente no tiene valor.
  5. En JRE 6, stop()verifica con el administrador de seguridad y luego llama a stop1()esas llamadas stop0(). stop0()es código nativo
  6. A partir de Java 13 Thread.stop()no se ha eliminado (todavía), pero Thread.stop(Throwable)se eliminó en Java 11. ( lista de correo , JDK-8204243 )

8

Votaría para Thread.stop().

Por ejemplo, tiene una operación duradera (como una solicitud de red). Supuestamente está esperando una respuesta, pero puede llevar tiempo y el usuario navegó a otra interfaz de usuario. Este hilo de espera ahora es a) inútil b) problema potencial porque cuando obtendrá el resultado, es completamente inútil y desencadenará devoluciones de llamada que pueden conducir a varios errores.

Todo eso y puede hacer un procesamiento de respuesta que podría ser intenso en la CPU. Y usted, como desarrollador, ni siquiera puede detenerlo, porque no puede lanzar if (Thread.currentThread().isInterrupted())líneas en todo el código.

Entonces, la incapacidad de detener con fuerza un hilo es extraño.


Si la operación de la red ya se puede cancelar de forma segura, simplemente interrumpa el hilo. Si la operación de la red no se puede cancelar de forma segura, no podría llamar de Thread.stop()forma segura de todos modos. No está votando Thread.stop(), está pidiendo a cada persona que implemente cada operación que pueda tomar mucho tiempo para que sea abortable de forma segura. Y eso puede ser una buena idea, pero no tiene nada que ver con la implementación Thread.stop()como la forma de solicitar el aborto seguro. Ya tenemos interruptpara eso.
David Schwartz el

"Así que la incapacidad de detener un hilo con fuerza es extraño". - ... hasta que mires profundamente (como lo han hecho los diseñadores de Java) y concluyas que no hay soluciones técnicamente viables que no sean peores que la depreciación stop.
Stephen C

5

La pregunta es bastante vaga. Si quisiste decir "¿cómo escribo un programa para que un hilo deje de ejecutarse cuando lo quiero?", Entonces otras respuestas deberían ser útiles. Pero si quiso decir "Tengo una emergencia con un servidor que no puedo reiniciar en este momento y solo necesito un subproceso particular para morir, pase lo que pase", entonces necesita una herramienta de intervención para que coincida con las herramientas de monitoreo como jstack.

Para este propósito creé jkillthread . Consulte sus instrucciones de uso.


¡Gracias! ¡Exactamente lo que estaba buscando!
Denis Kokorin

4

Por supuesto, existe el caso en el que está ejecutando algún tipo de código no completamente confiable. (Personalmente, tengo esto al permitir que las secuencias de comandos cargadas se ejecuten en mi entorno Java. Sí, hay una alarma de seguridad sonando en todas partes, pero es parte de la aplicación). respetar algún tipo de señal booleana de ejecución / no ejecución. Su único seguro decente es llamar al método de detención en el hilo si, por ejemplo, se ejecuta más de un tiempo de espera.

Pero, esto es simplemente "decente", y no absoluto, porque el código podría detectar el error ThreadDeath (o cualquier excepción que arrojes explícitamente), y no volver a lanzarlo como se supone que debe hacer un hilo caballeroso. Por lo tanto, la conclusión es AFAIA, no hay seguridad absoluta a prueba de fallas.


AFAIK cada vez más servicios se están volviendo híbridos y usan un entorno administrado para ejecutar código de terceros (plugins, scripts, etc.) que no tienen control total sobre el código, parece irrazonable quitar completamente el hilo. ya que para los ingenieros de servicio, un estado de servicio y servicio puede ser infinitamente mejor que un estado de servicio (ya sea debido a la suspensión (que elimina los hilos) o al bucle infinito ocupado (que elimina los núcleos))
Weipeng L

3

No hay forma de matar con gracia un hilo.

Puede intentar interrumpir el hilo, una estrategia común es usar una píldora venenosa para enviar un mensaje al hilo para que se detenga

public class CancelSupport {
    public static class CommandExecutor implements Runnable {
            private BlockingQueue<String> queue;
            public static final String POISON_PILL  = stopnow”;
            public CommandExecutor(BlockingQueue<String> queue) {
                    this.queue=queue;
            }
            @Override
            public void run() {
                    boolean stop=false;
                    while(!stop) {
                            try {
                                    String command=queue.take();
                                    if(POISON_PILL.equals(command)) {
                                            stop=true;
                                    } else {
                                            // do command
                                            System.out.println(command);
                                    }
                            } catch (InterruptedException e) {
                                    stop=true;
                            }
                    }
                    System.out.println(“Stopping execution”);
            }

    }

}

BlockingQueue<String> queue=new LinkedBlockingQueue<String>();
Thread t=new Thread(new CommandExecutor(queue));
queue.put(“hello”);
queue.put(“world”);
t.start();
Thread.sleep(1000);
queue.put(“stopnow”);

http://anandsekar.github.io/cancel-support-for-threads/


2

Por lo general, no mata, detiene ni interrumpe un hilo (ni verifica si está interrumpido ()), sino que deja que termine naturalmente.

Es simple. Puede usar cualquier bucle junto con la variable booleana (volátil) dentro del método run () para controlar la actividad del hilo. También puede volver del hilo activo al hilo principal para detenerlo.

De esta manera matas con gracia un hilo :).


1

Los intentos de terminación abrupta de hilos son malas prácticas de programación bien conocidas y evidencia de un diseño deficiente de la aplicación. Todos los subprocesos de la aplicación multiproceso comparten de manera explícita e implícita el mismo estado del proceso y se ven obligados a cooperar entre sí para mantenerlo consistente, de lo contrario, su aplicación será propensa a los errores que serán realmente difíciles de diagnosticar. Por lo tanto, es responsabilidad del desarrollador proporcionar una garantía de dicha coherencia mediante un diseño de aplicación cuidadoso y claro.

Hay dos soluciones correctas principales para las terminaciones de subprocesos controlados:

  • Uso de la bandera volátil compartida
  • Uso del par de métodos Thread.interrupt () y Thread.interrupted ().

Aquí se puede encontrar una explicación buena y detallada de los problemas relacionados con la terminación abrupta de subprocesos, así como ejemplos de soluciones incorrectas y correctas para la terminación de subprocesos controlados:

https://www.securecoding.cert.org/confluence/display/java/THI05-J.+Do+not+use+Thread.stop%28%29+to+terminate+threads


Dado que los programas ya no están escritos por un solo desarrollador, a menudo es necesario matar subprocesos. Por lo tanto, no se puede considerar una mala programación. No puedo imaginar Linux sin matar. La incapacidad para matar hilos es un defecto de Java.
Pavel Niedoba

1
Impresionante, Sr. Correcto ... ¿Qué sucede si tiene que llamar a una biblioteca de terceros, sobre la cual no tiene control y que tiene un tiempo de espera defectuoso y podría colgarse una vez cada 1000-2000 veces cuando se ejecuta? Mala práctica de programación ¿eh? Bueno, no siempre puedes tener acceso al código que estás usando. De todos modos, el operador pregunta cómo matar un hilo, no cómo controlar su flujo al diseñar su propio código ...
Arturas M

La pregunta es ¿qué sucederá cuando elimine un subproceso que tiene propiedad de mutex o tiene algún bloque de memoria asignado, o que debería generar algún evento o datos que otro subproceso está esperando? ¿Qué pasará con el resto de la lógica de su aplicación? Siempre estará en riesgo de que un problema pequeño y evidente se convierta en un problema complejo que será difícil de reproducir e investigar. Matar el hilo no es seguro porque puede dejar su aplicación en una cantidad de estados inconsistentes diferentes. Eche un vistazo a la información del enlace en cert.org.
ZarathustrA

Tenemos un hilo conductor que, en ciertas condiciones, puede decidir que todo el cálculo simplemente no tenga sentido. Los muchos runnables diferentes que hacen el trabajo real deben ser salpicados con alguna variante de isInterrupted () en cualquier lugar conveniente, ¡qué horrible! Es mucho mejor si el controlador hace que un ThreadDeath se levante aparentemente de la nada, desenrollando limpiamente todos los bloques sincronizados y finalmente. Todo el mundo dice que esto es muy propenso a errores, pero para los subprocesos no relacionados, no veo por qué.
Daniel


1

No conseguí que la interrupción funcionara en Android, así que utilicé este método, funciona perfectamente:

boolean shouldCheckUpdates = true;

private void startupCheckForUpdatesEveryFewSeconds() {
    Thread t = new Thread(new CheckUpdates());
    t.start();
}

private class CheckUpdates implements Runnable{
    public void run() {
        while (shouldCheckUpdates){
            //Thread sleep 3 seconds
            System.out.println("Do your thing here");
        }
    }
}

 public void stop(){
        shouldCheckUpdates = false;
 }

2
Se debe agregar una palabra clave volátil a shouldCheckUpdates, en caso de que el compilador se optimice con el almacenamiento local de subprocesos.
clinux

1

'Matar un hilo' no es la frase correcta para usar. Aquí hay una forma en que podemos implementar la finalización / salida elegante del hilo en will:

Runnable que utilicé:

class TaskThread implements Runnable {

    boolean shouldStop;

    public TaskThread(boolean shouldStop) {
        this.shouldStop = shouldStop;
    }

    @Override
    public void run() {

        System.out.println("Thread has started");

        while (!shouldStop) {
            // do something
        }

        System.out.println("Thread has ended");

    }

    public void stop() {
        shouldStop = true;
    }

}

La clase desencadenante:

public class ThreadStop {

    public static void main(String[] args) {

        System.out.println("Start");

        // Start the thread
        TaskThread task = new TaskThread(false);
        Thread t = new Thread(task);
        t.start();

        // Stop the thread
        task.stop();

        System.out.println("End");

    }

}

Se debe agregar una palabra clave volátil a la variable shouldStop, en caso de que el compilador se optimice con el almacenamiento local de subprocesos.
Oleksii Kyslytsyn

0

Thread.stop está en desuso, entonces, ¿cómo detenemos un hilo en Java?

Siempre use el método de interrupción y el futuro para solicitar la cancelación

  1. Cuando la tarea responde a la señal de interrupción, por ejemplo, el método de toma de cola de bloqueo.
Callable < String > callable = new Callable < String > () {
    @Override
    public String call() throws Exception {
        String result = "";
        try {
            //assume below take method is blocked as no work is produced.
            result = queue.take();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return result;
    }
};
Future future = executor.submit(callable);
try {
    String result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    logger.error("Thread timedout!");
    return "";
} finally {
    //this will call interrupt on queue which will abort the operation.
    //if it completes before time out, it has no side effects
    future.cancel(true);
}
  1. Cuando la tarea no responde a la señal de interrupción, suponga que la tarea realiza E / S de socket que no responde a la señal de interrupción y, por lo tanto, el uso del enfoque anterior no cancelará la tarea, el tiempo de espera expirará en el futuro, pero la cancelación en el bloque finalmente no tendrá efecto , el hilo seguirá escuchando el socket. Podemos cerrar el socket o llamar al método de cierre en la conexión si se implementa por pool.
public interface CustomCallable < T > extends Callable < T > {
    void cancel();
    RunnableFuture < T > newTask();
}

public class CustomExecutorPool extends ThreadPoolExecutor {
    protected < T > RunnableFuture < T > newTaskFor(Callable < T > callable) {
        if (callable instanceof CancellableTask)
            return ((CancellableTask < T > ) callable).newTask();
        else
            return super.newTaskFor(callable);
    }
}

public abstract class UnblockingIOTask < T > implements CustomCallable < T > {
    public synchronized void cancel() {
        try {
            obj.close();
        } catch (IOException e) {
            logger.error("io exception", e);
        }
    }

    public RunnableFuture < T > newTask() {
        return new FutureTask < T > (this) {
            public boolean cancel(boolean mayInterruptIfRunning) {
                try {
                    this.cancel();
                } finally {
                    return super.cancel(mayInterruptIfRunning);
                }
            }

        };
    }
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.