Полезный пример завершения работы в Java? - PullRequest
118 голосов
/ 27 мая 2010

Я пытаюсь убедиться, что мое 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 ]

150 голосов
/ 27 мая 2010

Вы можете сделать следующее:

  • Позвольте перехватчику завершения установить некоторый AtomicBoolean (или volatile логический) "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 () .

0 голосов
/ 30 августа 2015

Завершающие зацепки - это незапущенные потоки, которые зарегистрированы в Runtime.addShutdownHook (). JVM не дает никаких гарантий относительно порядка, в котором запускаются завершающие зацепки. Для получения дополнительной информации см. http://techno -terminal.blogspot.in /2015/08/shutdown-hooks.html

...