Полезный пример завершения работы в Java?
Я пытаюсь убедиться, что мое Java-приложение предпринимает разумные шаги, чтобы быть устойчивым, и часть этого включает в себя корректное завершение работы. Я читаю о хуках отключения и не понимаю, как их использовать на практике.
Есть ли практический пример?
Допустим, у меня было очень простое приложение, подобное приведенному ниже, которое записывает числа в файл, 10 в строку, партиями по 100, и я хочу убедиться, что данный пакет завершается, если программа прерывается. Я понимаю, как зарегистрировать обработчик завершения работы, но понятия не имею, как интегрировать это в мое приложение. Какие-либо предложения?
package com.example.test.concurrency;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
public class GracefulShutdownTest1 {
final private int N;
final private File f;
public GracefulShutdownTest1(File f, int N) { this.f=f; this.N = N; }
public void run()
{
PrintWriter pw = null;
try {
FileOutputStream fos = new FileOutputStream(this.f);
pw = new PrintWriter(fos);
for (int i = 0; i < N; ++i)
writeBatch(pw, i);
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
finally
{
pw.close();
}
}
private void writeBatch(PrintWriter pw, int i) {
for (int j = 0; j < 100; ++j)
{
int k = i*100+j;
pw.write(Integer.toString(k));
if ((j+1)%10 == 0)
pw.write('\n');
else
pw.write(' ');
}
}
static public void main(String[] args)
{
if (args.length < 2)
{
System.out.println("args = [file] [N] "
+"where file = output filename, N=batch count");
}
else
{
new GracefulShutdownTest1(
new File(args[0]),
Integer.parseInt(args[1])
).run();
}
}
}
2 ответа
Вы можете сделать следующее:
- Пусть хук выключения устанавливает для некоторого AtomicBoolean (или volatile boolean) "keepRunning" значение false
- (По выбору,
.interrupt
рабочие потоки, если они ждут данных в каком-то блокирующем вызове) - Ждите рабочих потоков (выполняющих
writeBatch
в вашем случае), чтобы закончить, позвонивThread.join()
метод на рабочих нитях. - Прекратить программу
Какой-то отрывочный код:
- Добавить
static volatile boolean keepRunning = true;
В run() вы меняете на
for (int i = 0; i < N && keepRunning; ++i) writeBatch(pw, i);
В main() вы добавляете:
final Thread mainThread = Thread.currentThread(); Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { keepRunning = false; mainThread.join(); } });
Примерно так я делаю изящное "отклонение всех клиентов при нажатии Control-C" в терминале.
Из документов:
Когда виртуальная машина начинает свою последовательность выключения, она запускает все зарегистрированные обработчики завершения работы в неустановленном порядке и позволяет им запускаться одновременно. Когда все перехватчики завершены, он запустит все непроверенные финализаторы, если финализация при выходе была включена. Наконец, виртуальная машина остановится.
То есть ловушка отключения поддерживает работу JVM до тех пор, пока ловушка не будет прервана (возвращена из метода run()).
Завершающие зацепки - это незапущенные потоки, которые зарегистрированы в Runtime.addShutdownHook().JVM не дает никаких гарантий относительно порядка, в котором запускаются завершающие зацепки. Для получения дополнительной информации см. http://techno-terminal.blogspot.in/2015/08/shutdown-hooks.html