На самом деле, вы не можете ... Единственный способ сделать это - использовать thread.stop, договориться о «кооперативном» методе (например, иногда проверять Thread.isInterrupted или вызывать метод, который выбрасывает InterruptedException, например Thread.sleep ()), или каким-либо образом вызвать метод в другой JVM полностью.
Для некоторых видов тестов вызов stop () - это нормально, но он, вероятно, повредит состоянию вашего набора тестов, поэтому вам придется перезапускать JVM после каждого вызова stop (), если вы хотите избежать взаимодействия эффекты.
Чтобы получить хорошее описание того, как реализовать совместный подход, ознакомьтесь с Часто задаваемыми вопросами Sun об устаревших методах Thread .
В качестве примера такого подхода в реальной жизни Объект задания API-интерфейса Eclipse RCP IProgressMonitor позволяет некоторым службам управления сигнализировать подпроцессам (с помощью метода «отмена»), что они должны остановиться. Конечно, это зависит от методов, которые регулярно проверяют метод isCancelled, чего они часто не в состоянии сделать.
Гибридный подход может заключаться в том, чтобы красиво задать потоку прерывание, а затем через пару секунд настаивать на остановке. Опять же, вы не должны использовать stop в рабочем коде, но в этом случае все может быть хорошо, особенно. если вы вскоре выйдете из JVM.
Чтобы проверить этот подход, я написал простой жгут, который берет работоспособный объект и пытается его выполнить. Не стесняйтесь комментировать / редактировать.
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");
}
Чтобы проверить это, я написал два конкурирующих бесконечных цикла, один кооператив и тот, который никогда не проверяет прерванный бит своего потока.
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");
}
}
Наконец, я написал тесты (JUnit 4) для их проверки:
@Test
public void testStopCooperative() {
testStop(new Runnable() {
@Override
public void run() {
cooperative();
}
});
}
@Test
public void testStopNoncooperative() {
testStop(new Runnable() {
@Override
public void run() {
noncooperative();
}
});
}
Раньше я никогда не использовал Thread.stop (), поэтому я не знал о его работе. Он работает, выбрасывая объект ThreadDeath от того места, где в данный момент работает целевой поток. Это расширяет ошибку. Таким образом, хотя он не всегда работает чисто, он обычно оставляет простые программы с довольно разумным состоянием программы. Например, любые блоки finally называются. Если вы хотите быть настоящим придурком, вы можете поймать ThreadDeath (или Ошибка) и продолжать работать, в любом случае!
Если больше ничего, это действительно заставляет меня желать, чтобы больше кода следовало подходу IProgressMonitor - добавление еще одного параметра к методам, которые могут занять некоторое время, и побуждение разработчика метода периодически опрашивать объект Monitor для посмотрим, хочет ли пользователь отказаться от системы. Я постараюсь следовать этому шаблону в будущем, особенно методы, которые могут быть интерактивными. Конечно, вы не обязательно заранее знаете, какие методы будут использоваться таким образом, но для этого, я полагаю, и нужны профилировщики.
Что касается метода «начать другую JVM полностью», он потребует больше работы. Я не знаю, написал ли кто-нибудь загрузчик делегирующего класса или он включен в JVM, но это потребуется для этого подхода.