Realmente, no se puede ... La única manera de hacerlo es usar thread.stop, acordar un método "cooperativo" (por ejemplo, comprobar ocasionalmente si Thread.isinterrupted o llamar a un método que arroja una interruptedException , por ejemplo, Thread.sleep()), o de alguna manera invocar el método en otra JVM por completo.
Para ciertos tipos de pruebas, llamar a stop() está bien, pero probablemente dañará el estado de su conjunto de pruebas, por lo que tendrá que reiniciar la JVM después de cada llamada para detener() si desea evitar efectos de interacción.
Para obtener una buena descripción de cómo implementar el enfoque cooperativo, consulte Sun's FAQ on the deprecated Thread methods.
Para un ejemplo de este enfoque en la vida real, el objeto 'IProgressMonitor' Eclipse RCP's Job API's permite que algunos servicios de administración señalicen los subprocesos (a través del método 'cancelar') que deben detenerse. Por supuesto, eso depende de los métodos para verificar realmente el método isCancelled con regularidad, que a menudo no pueden hacer.
Un enfoque híbrido podría ser preguntarle al hilo muy bien con la interrupción, luego insistir un par de segundos más tarde con la parada. De nuevo, no debería usar detenerse en el código de producción, pero podría estar bien en este caso, esp. si sale de la JVM poco después.
Para probar este enfoque, escribí un arnés simple, que toma un ejecutable e intenta ejecutarlo. Siéntase libre de comentar/editar.
public void testStop(Runnable r) {
Thread t = new Thread(r);
t.start();
try {
t.join(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (!t.isAlive()) {
System.err.println("Finished on time.");
return;
}
try {
t.interrupt();
t.join(2000);
if (!t.isAlive()) {
System.err.println("cooperative stop");
return;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.err.println("non-cooperative stop");
StackTraceElement[] trace = Thread.getAllStackTraces().get(t);
if (null != trace) {
Throwable temp = new Throwable();
temp.setStackTrace(trace);
temp.printStackTrace();
}
t.stop();
System.err.println("stopped non-cooperative thread");
}
Para probarlo, me escribió dos compiten bucles infinitos, una cooperativa, y uno que nunca se comprueba poco interrumpida de su hilo.
public void cooperative() {
try {
for (;;) {
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.err.println("cooperative() interrupted");
} finally {
System.err.println("cooperative() finally");
}
}
public void noncooperative() {
try {
for (;;) {
Thread.yield();
}
} finally {
System.err.println("noncooperative() finally");
}
}
Finalmente, escribí las pruebas (JUnit 4) ejercer ellos:
@Test
public void testStopCooperative() {
testStop(new Runnable() {
@Override
public void run() {
cooperative();
}
});
}
@Test
public void testStopNoncooperative() {
testStop(new Runnable() {
@Override
public void run() {
noncooperative();
}
});
}
que nunca había utilizado Thread.stop() antes, así que no estaba al tanto de su funcionamiento. Funciona lanzando un objeto ThreadDeath desde donde se está ejecutando actualmente el hilo objetivo. Esto extiende el error. Entonces, si bien no siempre funciona limpiamente, generalmente dejará programas simples con un estado de programa bastante razonable. Por ejemplo, se llama a cualquier bloque final. Si quisieras ser un verdadero imbécil, podrías capturar ThreadDeath (o Error), ¡y seguir funcionando!
Por lo menos, esto realmente me hace desear más código siguió el enfoque IProgressMonitor - agregando otro parámetro a los métodos que podría tardar un rato, y fomentar el implementador del método para sondear vez en cuando el objeto de monitor para ver si el el usuario quiere que el sistema se rinda. Trataré de seguir este patrón en el futuro, especialmente los métodos que podrían ser interactivos. Por supuesto, no necesariamente sabes de antemano qué métodos se utilizarán de esta manera, pero para eso son los Profilers, supongo.
En cuanto al método 'iniciar otra JVM por completo', eso requerirá más trabajo. No sé si alguien ha escrito un cargador de clase delegante, o si uno está incluido en la JVM, pero eso sería necesario para este enfoque.
gracias por su respuesta. en este caso, la función no puede diseñarse para verificar periódicamente una variable o primitiva de sincronización. – carrier