1. 应用场景举例
有一个视频处理的java工程,需要调用ffmpeg的转码方法(不要问为什么不用python,用python我还写啥;不要问为什么不用javacv,我就是想自己尝试一下java调用命令行),如下:
Runtime.getRuntime().exec("ffmpeg -i test.mp4 -c copy -bsf:v h264_mp4toannexb -hls_time 3 enctest.m3u8");
这就是在java主进程中开了一个子进程。正常来说,当主进程结束的时候,应该同时关闭这些子进程。
- 1)关闭了主进程的时候子进程并不会自动关闭
- 2)如果不及时关闭子进程,大量子进程严重占用资源
为了关闭这些子进程,我们有两种选择来:
- 1)查看系统进程,手动(或脚本)杀死所有由这个主进程打开的子进程;
- 2)在主进程关闭的时候,采取一些方法关闭子进程
我选择第2种方法。
我们知道javabean有@PreDestroy
方法用来在对象销毁之前执行一些操作,很容易想到进程应该也有类似的方法。这就是
Runtime.getRuntime().addShutdownHook("进程关闭之前执行的方法");
我们称这个东西为“关闭钩子”。在主进程正常结束(什么是正常结束?往下看)的时候,这个关闭钩子就会在进程结束前执行一次。
给一个例子:
public class HookTest {
private static final void shutdownCallback() {
System.out.println("Shutdown callback is invoked");
}
public static void main(String[] args) throws InterruptedException {
Runtime.getRuntime().addShutdownHook(new Thread(() -> shutdownCallback())); // 这里用了java的新特性lambda,其实就是开一个线程
Thread.sleep(10000);
}
}
这个例子,正常结束(这里又提到了正常结束,往下看),就会输出:
Shutdown callback is invoked.
看到这里,至少你应该明白了一件事,在这个“关闭钩子”里面,我们可以进行临时文件清理、子进程关闭、通知其他程序本程序即将关闭。。。。这些工作。这样一个java进程就能被正常关闭。
标题“正确地关闭进程”里面“正确”的意思就是让进程正常关闭。
2. 什么是正常结束?
正常结束就是程序运行到最后自然推出?
不是。
正常技术包含如下几项:
- 1)
System.exit()
- 2)
Ctrl+C
- 3)
OutOfMemoryError
- 4)
kill [pid]
(kill -9
这种直接结束进程的不是,kill -15
可以)
通常来说,我们记得2和4就行,分别对应的是jar包在shell中直接执行和后台执行的关闭方法。
这时候你可能会有一个问题(也可能你早就知道了),在IDE中运行时,怎么办?我难道测试关闭钩子的时候还得打包去运行?(这也是一个好办法,我看有的大佬推荐调试程序就是用的jar包而不是IDE,别问我为什么,我不知道)有一个更简单的方法,使用IDE“运行”面板中的退出按钮,而不是停止按钮,以idea举例,停止按钮就是我们经常点的红色方块按钮,退出按钮是一个方框带一个红色箭头。如下:
参考: