invoking - stop run thread java
¿Están Thread.stop y sus amigos seguros en Java? (8)
Aquí está mi intento de responder mi propia pregunta.
Creo que las siguientes condiciones deberían ser suficientes para que un solo hilo se detenga de forma segura utilizando Thread.stop()
:
- La ejecución del subproceso no debe crear ni mutar ningún estado (es decir, objetos Java, variables de clase, recursos externos) que puedan ser visibles para otros subprocesos en el caso de que el subproceso se detenga.
- La ejecución del hilo no debe usar la
notify
a ningún otro hilo durante su ejecución normal. - El hilo no debe
start
nijoin
otros hilos, ni interactuar con, luego, usarstop
,suspend
oresume
.
(El término ejecución de subprocesos anterior cubre todo el código de nivel de aplicación y todo el código de biblioteca que ejecuta el subproceso).
La primera condición significa que un hilo detenido no dejará ninguna estructura de datos externa o recursos en un estado inconsistente. Esto incluye estructuras de datos a las que podría estar accediendo (leyendo) dentro de un mutex. La segunda condición significa que un subproceso que se puede detener no puede dejar otro subproceso en espera. Pero también prohíbe el uso de cualquier mecanismo de sincronización que no sea un simple objeto de exclusión mutua.
Un subproceso que se puede detener debe tener una forma de entregar los resultados de cada cálculo al subproceso de control. Estos resultados son creados / mutados por el subproceso que se puede detener, por lo que simplemente debemos asegurarnos de que no estén visibles después de una detención del hilo. Por ejemplo, los resultados podrían asignarse a miembros privados del objeto Thread y "guardarse" con un indicador que está atómicamente por el hilo para decir que está "hecho".
EDITAR : Estas condiciones son bastante restrictivas. Por ejemplo, para que un subproceso del "evaluador de expresiones regulares" se detenga de forma segura, si debemos garantizar que el motor de expresiones regulares no mute ningún estado visible externamente. ¡El problema es que podría hacerlo, dependiendo de cómo implementes el hilo!
- Los
Pattern.compile(...)
podrían actualizar un caché estático de patrones compilados, y si lo hicieran, deberían (deberían) usar un mutex para hacerlo. (En realidad, la versión OpenJDK 6.0 no almacena patrones en caché, pero es posible que Sun pueda cambiar esto). - Si intenta evitar 1) compilando la expresión regular en el hilo de control y suministrando un
Matcher
pre-instanciado, entonces el hilo de expresión regular muta el estado visible externamente.
En el primer caso, probablemente estemos en problemas. Por ejemplo, supongamos que se utilizó un HashMap para implementar el caché y que el hilo se interrumpió mientras se reorganizaba el HashMap.
En el segundo caso, estaríamos bien si el Matcher
no se hubiera pasado a algún otro hilo, y siempre que el hilo del controlador no intentara usar el Matcher
después de detener el hilo del emparejador de expresiones regulares.
¿A dónde nos lleva esto?
Bueno, creo que he identificado condiciones bajo las cuales los hilos son teóricamente seguros de detener. También creo que es teóricamente posible analizar estáticamente el código de un hilo (y los métodos a los que llama) para ver si estas condiciones siempre se cumplirán. Pero, no estoy seguro de si esto es realmente práctico.
¿Esto tiene sentido? ¿Me he perdido algo?
Editar 2
Las cosas se ponen un poco más complicadas si consideras que el código que podríamos estar intentando eliminar podría no ser confiable:
No podemos confiar en las "promesas"; por ejemplo, anotaciones en el código que no es de confianza que puede ser ejecutable o no.
En realidad, debemos ser capaces de evitar que el código que no es de confianza haga cosas que lo harían impensable ... de acuerdo con los criterios identificados.
Sospecho que esto implicaría modificar el comportamiento de la JVM (por ejemplo, implementando restricciones de tiempo de ejecución, qué subprocesos pueden bloquear o modificar), o una implementación completa de los Aislados JSR. Eso está más allá del alcance de lo que estaba considerando como "juego justo".
Así que vamos a descartar el caso de código no confiable por ahora. O al menos, reconozca que el código malintencionado puede hacer cosas para que no se pueda ejecutar de forma segura, y poner ese problema a un lado.
Las stop()
, suspend()
y resume()
en java.lang.Thread
están en desuso porque no son unsafe . La Thread.interrupt()
recomendada por Sun es utilizar Thread.interrupt()
, pero ese enfoque no funciona en todos los casos. Por ejemplo, si llama a un método de biblioteca que no comprueba explícita o implícitamente el indicador interrupted
, no tiene más remedio que esperar a que finalice la llamada.
Entonces, me pregunto si es posible caracterizar situaciones en las que es (probablemente) seguro llamar a stop()
en un hilo. Por ejemplo, ¿sería seguro stop()
un subproceso que no hizo nada más que llamar a find(...)
o match(...)
en un java.util.regex.Matcher
?
(Si hay ingenieros de Sun leyendo esto ... una respuesta definitiva sería muy apreciada).
EDITAR : Respuestas que simplemente replantean el mantra que no debe llamar a stop()
porque está en desuso, inseguro, lo que falta el punto de esta pregunta. Sé que es realmente inseguro en la mayoría de los casos, y que si hay una alternativa viable, siempre debe usarla en su lugar.
Esta pregunta es sobre los casos de subconjuntos donde es seguro. Específicamente, ¿qué es ese subconjunto?
Hay formas de usar Thread.stop () relativamente estables sin memoria con fugas o descriptores de archivos (los FD son excepcionalmente propensos a fugas en * NIX), pero debe confiar en ellos solo si está obligado a administrar el código de terceros. Nunca lo use para lograr el resultado si puede tener control sobre el código en sí.
Si utilizo Thread.stop junto con w / interrupt () y algunas cosas más de hacks, como agregar controladores de registro personalizados para volver a lanzar el ThreadDeath atrapado, agregar unhandleExceltionHandler, ejecutando tu propio ThreadGroup (sincronización sobre ellos), etc.
Pero eso merece un tema completamente nuevo.
Pero en este caso es lo que te dicen los diseñadores de Java; y son más autoritarios en su idioma que cualquiera de nosotros :)
Solo una nota: algunos de ellos son bastante despistados
La falta de seguridad proviene de la idea idea de secciones críticas.
Take mutex
do some work, temporarily while we work our state is inconsistent
// all consistent now
Release mutex
Si quitas el hilo y sucede que está en una sección crítica, entonces el objeto se queda en un estado inconsistente, lo que significa que no se puede usar de forma segura desde ese punto.
Para que sea seguro eliminar el hilo, debe comprender todo el procesamiento de lo que se está haciendo en ese hilo, para saber que no hay tales secciones críticas en el código. Si está utilizando el código de la biblioteca, es posible que no pueda ver la fuente y sepa que es seguro. Incluso si es seguro hoy, puede que no sea mañana.
(Muy artificial) Ejemplo de posible inseguridad. Tenemos una lista enlazada, no es cíclica. Todos los algoritmos son realmente rápidos porque sabemos que no es cíclico. Durante nuestra sección crítica introducimos temporalmente un ciclo. Luego nos quedamos impresionados antes de salir de la sección crítica. Ahora todos los algoritmos usan el bucle de lista para siempre. ¡Ningún autor de la biblioteca haría eso seguramente! ¿Cómo lo sabes? No puedes asumir que el código que usas está bien escrito.
En el ejemplo que señala, seguramente es posible escribir la funcionalidad requreid de forma interrumpible. Más trabajo, pero es posible estar seguro.
Tomaré un folleto: no hay un subconjunto documentado de Objetos y métodos que puedan usarse en subprocesos cancelables, porque ningún autor de la biblioteca quiere hacer las garantías.
Las primitivas de sincronización de Java pueden proporcionar todas las formas de control de concurrencia mediante la construcción de controls concurrency más complex que se adapten a su problema.
Las razones de la desaprobación se indican claramente en el enlace que proporciona. Si está dispuesto a aceptar los motivos, puede utilizar esas funciones.
Sin embargo, si elige usar esas características, también acepta que el soporte para esas características podría detenerse en cualquier momento.
Edición: Reitero la razón de la desaprobación y cómo evitarlos.
Dado que el único peligro es que los objetos a los que puede hacer referencia el subproceso stop
pueden corromperse, simplemente clone
la String
antes de pasarla al Thread
. Si no existen objetos compartidos, la amenaza de objetos corruptos en el programa fuera del Thread
stop
ya no existe.
No hay una forma segura de matar un hilo.
Tampoco hay un subconjunto de situaciones donde sea seguro. Incluso si está funcionando al 100% mientras prueba en Windows, puede dañar la memoria del proceso JVM en Solaris o perder recursos de subprocesos en Linux.
Siempre se debe recordar que debajo del subproceso de Java hay un subproceso real, nativo e inseguro.
Ese hilo nativo funciona con estructuras de control y datos nativos, de bajo nivel. Matarlo puede dejar esas estructuras de datos nativos en un estado no válido, sin una forma de recuperación.
No hay forma de que la máquina Java tenga en cuenta todas las posibles consecuencias, ya que el subproceso puede asignar / usar recursos no solo dentro del proceso JVM, sino también dentro del kernel del sistema operativo.
En otras palabras, si la biblioteca de subprocesos nativos no proporciona una forma segura de eliminar () un subproceso, Java no puede proporcionar ninguna garantía mejor que eso. Y todas las implementaciones nativas que conozco afirman que matar hilos es un asunto peligroso.
Si mi comprensión es correcta, el problema tiene que ver con los bloqueos de sincronización que no se liberan, ya que la ThreadInterruptedException () generada se propaga por la pila.
Teniendo eso en cuenta, es intrínsecamente inseguro porque nunca se puede saber si se invocó y se realizó alguna "llamada interna de método" en la que se encontraba en el momento mismo en que se detuvo () se realizó, se mantuvo efectivamente un bloqueo de sincronización, y luego qué Los ingenieros de Java dicen que es, aparentemente, inequívocamente correcto.
Lo que personalmente no entiendo es por qué debería ser imposible liberar un bloqueo de sincronización ya que este tipo particular de Excepción se propaga por la pila, pasando así todos los delimitadores de bloque de sincronización / método ''}'', lo que hace que se liberen los bloqueos Para cualquier otro tipo de excepción.
Tengo un servidor escrito en java, y si el administrador de ese servicio quiere un "apagado en frío", entonces es NECESARIO poder detener toda la actividad en ejecución sin importar qué. La consistencia del estado de cualquier objeto no es una preocupación porque todo lo que estoy tratando de hacer es SALIR. Tan rápido como pueda.
Tal vez haya algo que no sé, pero como java.sun.com dijo, no es seguro porque cualquier cosa que este hilo esté manejando está en grave riesgo de dañarse. Otros objetos, conexiones, archivos abiertos ... por razones obvias, como "no cierre su palabra sin guardar primero".
Para este ejemplo de find(...)
, realmente no creo que sea una catástrofe simplemente patearlo con un sutil .stop()
...
Un ejemplo concreto probablemente ayudaría aquí. Si alguien puede sugerir una buena alternativa al siguiente uso de stop, estaría muy interesado. Volver a escribir java.util.regex para admitir la interrupción no cuenta.
import java.util.regex.*;
import java.util.*;
public class RegexInterruptTest {
private static class BadRegexException extends RuntimeException { }
final Thread mainThread = Thread.currentThread();
TimerTask interruptTask = new TimerTask() {
public void run() {
System.out.println("Stopping thread.");
// Doesn''t work:
// mainThread.interrupt();
// Does work but is deprecated and nasty
mainThread.stop(new BadRegexException());
}
};
Timer interruptTimer = new Timer(true);
interruptTimer.schedule(interruptTask, 2000L);
String s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab";
String exp = "(a+a+){1,100}";
Pattern p = Pattern.compile(exp);
Matcher m = p.matcher(s);
try {
System.out.println("Match: " + m.matches());
interruptTimer.cancel();
} catch(BadRegexException bre) {
System.out.println("Oooops");
} finally {
System.out.println("All over");
}
}
}