Java关闭挂钩– Runtime.addShutdownHook()
程序退出时,使用Java shutdown钩子可以方便地运行一些代码。
我们可以使用java.lang.Runtime.addShutdownHook(Thread t)方法在JVM中添加一个关闭钩子。
Java关闭钩子
在这两种情况下都将运行Java shutdown hook。
- 程序正常退出,或者我们调用 - System.exit()方法终止程序。
 阅读有关Java系统类的更多信息。
- 用户中断,例如Ctrl + C,系统关闭等。 
有关Java Shutdown Hook的要点是:
- 我们可以使用Runtime的addShutdownHook()方法添加多个关闭钩子。 
- 关闭挂钩是已初始化但未启动的线程。 
 它们在JVM关闭触发时启动。
- 我们无法确定关机挂钩的执行顺序,就像执行多线程一样。 
- 如果已启用退出完成,则将执行所有未调用的终结器。 
- 无法保证关机钩子会执行,例如系统崩溃,kill命令等。 
 因此,仅应将其用于紧急情况下,例如确保释放关键资源等。
- 您可以使用Runtime.getRuntime()。 
 removeShutdownHook(hook)方法删除钩子。
- 启动关闭挂钩后,将无法删除它们。 
 您将得到- IllegalStateException。
- 如果存在安全管理器并且它拒绝 - RuntimePermission(" shutdownHooks"),则将获得SecurityException。
Java关闭挂钩示例
因此,让我们来看一下Java中的shutdown钩子示例。
这是一个简单的程序,其中我从某个目录中逐行读取文件并进行处理。
我将程序状态保存在静态变量中,以便关机挂钩可以访问它。
package com.theitroad.shutdownhook;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
public class FilesProcessor {
	public static String status = "STOPPED";
	public static String fileName = "";
	public static void main(String[] args) {
		String directory = "/Users/hyman/temp";
		Runtime.getRuntime().addShutdownHook(new ProcessorHook());
		File dir = new File(directory);
		File[] txtFiles = dir.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				if (name.endsWith(".txt"))
					return true;
				else
					return false;
			}
		});
		for (File file : txtFiles) {
			System.out.println(file.getName());
			BufferedReader reader = null;
			status = "STARTED";
			fileName = file.getName();
			try {
				FileReader fr = new FileReader(file);
				reader = new BufferedReader(fr);
				String line;
				line = reader.readLine();
				while (line != null) {
					System.out.println(line);
					Thread.sleep(1000); //assuming it takes 1 second to process each record
					//read next line
					line = reader.readLine();
				}
				status = "PROCESSED";
			} catch (IOException | InterruptedException e) {
				status = "ERROR";
				e.printStackTrace();
			}finally{
				try {
					reader.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		status="FINISHED";
	}
}
上面代码中最重要的部分是第16行,我们其中添加了shutdown钩子,下面是实现类。
package com.theitroad.shutdownhook;
public class ProcessorHook extends Thread {
	@Override
	public void run(){
		System.out.println("Status="+FilesProcessor.status);
		System.out.println("FileName="+FilesProcessor.fileName);
		if(!FilesProcessor.status.equals("FINISHED")){
			System.out.println("Seems some error, sending alert");
		}
		
	}
}
这是一种非常简单的用法,我只是在记录关闭钩子启动时的状态,如果还没有完成,则发送警报(实际上不是在这里记录它)。
让我们看看通过终端执行一些程序。
- 程序使用Ctrl + C命令终止。 
 如上图所示,当我们尝试使用Ctrl + C命令杀死JVM时,shutdown hook开始执行。
- 程序正常执行。 
 请注意,即使在正常退出的情况下,钩子也会被调用,并且打印状态为完成。
- Kill Command终止JVM。 
 我使用了两个终端窗口向Java程序发射kill命令,因此操作系统终止了JVM,在这种情况下,未执行关闭钩子。

