Поскольку у вас есть доступ к этим потокам (я имею в виду, что вы можете изменить их код), вы можете:
- Не разрешать пользователю прекращать выход из приложения обычным способом.Вы остановите это для него, как только он попросит вас сделать это.Это где вам нужен крюк.Например, если приложение основано на
JFrame
, тогда добавьте хук windowClosing()
, чтобы изящно остановить выполнение Thread
с и в то же время установить для операции закрытия кадра по умолчанию значение DO_NOTHING_ON_CLOSE .Посмотрите на мой код в этом ответе ниже ... - Поместите остановочный флаг в бесконечные петли, делая их конечными .
- Когда произойдет отключение, измените эти флаги так, чтобы каждый
Thread
выходил из цикла. - Когда вне цикла
Thread
внесет любые необходимые изменения (с полным доступом ко всем его переменным)) и затем выйдите из метода run()
. - Между тем, в основных
Thread
(т. е. Thread
, которые отмечали все остальные Thread
s для остановки), вы собираетесь join()
каждый конечный Thread
.
Этот метод позволяет дождаться полного выполнения Thread
(т. Е. Выйти из метода run()
). - Наконец, основной
Thread
, собирается вызвать , например, System.exit(0);
, чтобы выйти из всего приложения, потому что это, предположительно, последнее желаемое действие пользователя.
Примечание: предполагается, что пользовательсобирается закрыть приложение с обычными действиями.Если, например, пользователь собирается, например, убить приложение через диспетчер задач Windows, этот подход не будет работать.Я не знаю о перехватах отключения на Thread
s ...
Следующий пример кода (читайте комментарии):
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class GraceDown {
private static class MyThread extends Thread {
private boolean keepGoing = true; //This is the stopping flag.
private final int i; //In this case i marks the 'id' of the Thread, but it is also an arbitary variable that the Thread has acccess in its final moments...
public MyThread(final int i) {
this.i = i;
}
private synchronized boolean isKeepGoing() {
return keepGoing; //Tells if we should exit the loop and start the stopping operation...
}
public synchronized void startShutdown() {
keepGoing = false; //Tells we should exit the loop and start the stopping operation.
}
@Override
public void run() {
while (isKeepGoing()) { //After every complete loop, we check the if we can go on.
//Your 'infinite' loop actions go in here:
try { Thread.sleep(1000); } catch (final InterruptedException ie) {}
System.out.println("Thread " + i + " running...");
}
//Your gracefull shutdown actions go here...
System.out.println("Thread " + i + ": my stopping actions go here! Look, I have access to all my variables (such as i)! ;)");
}
}
public static void main(final String[] args) {
//Create and start the Threads:
final MyThread[] myThreads = new MyThread[5];
for (int i = 0; i < myThreads.length; ++i)
myThreads[i] = new MyThread(i);
for (int i = 0; i < myThreads.length; ++i)
myThreads[i].start();
final JFrame frame = new JFrame("Lets close me...");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); //IMPORTANT STEP! This means we are going to close the application instead of it being closed automatically when the user presses to close the window...
//Adding the 'hook':
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(final WindowEvent wevt) {
System.out.println("Initializing shutdown...");
//This is where the thread is going to stop all others...
for (int i = 0; i < myThreads.length; ++i)
myThreads[i].startShutdown();
//Wait for all threads to stop:
for (int i = 0; i < myThreads.length; ++i)
try { myThreads[i].join(); } catch (final InterruptedException ie) { System.err.println("Error gracefully shutdowning the Thread!!!"); }
//Now we can exit the JVM:
System.out.println("Exiting JVM...");
System.exit(0); //Code '0' indicates exiting without error(s).
}
});
frame.getContentPane().add(new JLabel("Close this window and the Threads will shutdown gracefully...", JLabel.CENTER));
frame.pack();
frame.setLocationRelativeTo(null); //Put the packed frame on the center of the default screen.
frame.setVisible(true);
}
}
Вышеприведенное должно напечатать что-то вроде этого:
Тема 1 запущена ...
Тема 4 запущена ...
Тема 0 запущена ...
Тема 2 запущена ...
Тема 3 запущена ...
Тема3 работает ...
Поток 4 запущен ...
Поток 2 запущен ...
Поток 1 запущен ...
Поток 0 запущен ...
Инициализация завершения ...
Тема 2 запущена ...
Тема 2: мои действия по остановке идут сюда!Смотри, у меня есть доступ ко всем моим переменным (например, я)!;)
Тема 0 запущена ...
Тема 0: мои действия по остановке идут здесь!Смотри, у меня есть доступ ко всем моим переменным (например, я)!;)
Тема 1 запущена ...
Тема 4 запущена ...
Тема 3 запущена ...
Тема 3: мои действия по остановке идут здесь!Смотри, у меня есть доступ ко всем моим переменным (например, я)!;)
Тема 4: мои останавливающие действия идут сюда!Смотри, у меня есть доступ ко всем моим переменным (например, я)!;)
Тема 1: мои останавливающие действия идут сюда!Смотри, у меня есть доступ ко всем моим переменным (например, я)!;)
Выход из JVM ...
Это минимальное решение, которое я вижу в вашей проблеме, как вы ее описали.