Корисний приклад вимикання гачка на Java?


126

Я намагаюся переконатися, що моя програма 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();
        }
    }
}

Дивіться також stackoverflow.com/questions/8722826 / ...
flow2k

Відповіді:


160

Ви можете зробити наступне:

  • Нехай гак відключення встановить деяку AtomicBoolean (або летючу булеву) "KeepRunning" на false
  • (За бажанням, .interruptробочі потоки, якщо вони чекають даних у якомусь блокуванні виклику)
  • Зачекайте, поки робочі потоки (виконуючи writeBatchу вашому випадку) закінчуються, зателефонувавши Thread.join()методу на робочі потоки.
  • Припиніть програму

Деякі схематичні коди:

  • Додати а static volatile boolean keepRunning = true;
  • У run () ви переходите на

    for (int i = 0; i < N && keepRunning; ++i)
        writeBatch(pw, i);
  • В основному () ви додаєте:

    final Thread mainThread = Thread.currentThread();
    Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() {
            keepRunning = false;
            mainThread.join();
        }
    });

Це приблизно так, як я роблю витончене "відхиляти всіх клієнтів при натисканні на Control-C" в терміналі.


З документів :

Коли віртуальна машина розпочне свою послідовність відключення, вона запустить усі зареєстровані гаки відключення в певному невстановленому порядку і дозволить їм працювати одночасно. Коли всі гачки закінчені, він запускатиме всі незвані фіналізатори, якщо фіналізація при виході була включена. Нарешті, віртуальна машина зупиниться.

Тобто, гак відключення підтримує роботу JVM до тих пір, поки гак не припиниться (повернуто з методу run () .


14
Так, якщо я правильно розумію, суть того, що ви говорите, полягає в тому, що гак відключення має можливість спілкуватися з іншими поточними потоками потоку, а також відключити відключення VM (наприклад, використовуючи Thread.join () ), поки не переконається, що швидке очищення було завершено. Ось у чому я пропав. Дякую!
Джейсон S

2
Так. Ти маєш це. Оновили відповідь кількома реченнями з док.
aioobe

1
Вибачте заздалегідь, але не варто GracefulShutdownTest1 реалізувати Runnable?
Віктор

Чи буде це також працювати, GUI був запущений в основній темі? Я знаю, що це не найкращий спосіб зробити це (замість цього використовувати EDT), але я працюю над старим кодом.
Агостіно

1
@ MartynasJusevičius, JVM припиняється, коли всі недемонові потоки закінчуються. Якщо mainThreadце демонова нитка, то joinгарантуємо, що ми будемо чекати, коли вона закінчиться, перш ніж зупинити JVM.
aioobe

-9

Гачки відключення - це непроменені нитки, які зареєстровані Runtime.addShutdownHook (). JVM не дає жодної гарантії на порядок запуску гаків для вимкнення. Для отримання додаткової інформації зверніться до http://techno-terminal.blogspot.in/2015/08 /shutdown-hooks.html

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.