【问题标题】:Useful example of a shutdown hook in Java?Java中关闭挂钩的有用示例?
【发布时间】:2010-05-27 14:20:58
【问题描述】:

我正在努力确保我的 Java 应用程序采取合理的步骤来保持稳健,其中一部分涉及正常关闭。我正在阅读有关 shutdown hooks 的信息,但实际上我并不知道如何在实践中使用它们。

有实际的例子吗?

假设我有一个像下面这样一个非常简单的应用程序,它将数字写入文件,将 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();
        }
    }
}

【问题讨论】:

标签: java shutdown-hook


【解决方案1】:

您可以执行以下操作:

  • 让关闭挂钩将一些 AtomicBoolean(或 volatile boolean)“keepRunning”设置为 false
  • (可选,.interrupt 工作线程,如果它们在某些阻塞调用中等待数据)
  • 通过在工作线程上调用 Thread.join() 方法等待工作线程(在您的情况下执行 writeBatch)完成。
  • 终止程序

一些粗略的代码:

  • 添加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 时拒绝所有客户端”的方式。


来自the docs:

当虚拟机开始其关闭序列时,它将以某种未指定的顺序启动所有已注册的关闭挂钩并让它们同时运行。 当所有钩子都完成后,如果已启用 finalization-on-exit,它将运行所有未调用的终结器。最后,虚拟机将停止。

也就是说,关闭钩子让 JVM 一直运行,直到钩子终止(从 run() 方法返回。

【讨论】:

  • 啊——所以如果我理解正确,你所说的要点是关闭钩子有机会与其他当前运行的线程通信,并推迟虚拟机关闭(例如,通过使用 Thread.join()) 直到对快速清理完成感到满意为止。这就是我错过的重点。谢谢!
  • 是的。你说对了。用文档中的几句话更新了答案。
  • 抱歉,GracefulShutdownTest1不应该实现Runnable吗?
  • 如果在主线程中启动了 GUI,这也能工作吗?我知道这不是最好的方法(改用 EDT),但我正在处理旧代码。
  • @MartynasJusevičius,当所有非守护线程都终止时,JVM 终止。如果mainThread 是一个守护线程,那么join 保证我们在让JVM 关闭之前等待它完成。
【解决方案2】:

Shutdown Hooks 是在 Runtime.addShutdownHook() 中注册的未启动线程。JVM 不保证 shutdown hooks 的启动顺序。有关更多信息,请参阅 http://techno-terminal.blogspot.in/2015/08/shutdown-hooks.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多