Java应用程序退出的触发机制有:
- 自动结束:应用没有存活线程或只有后台线程时;
- System.exit(0);
- kill 或 ctrl+C;
- kill -9 强制退出;
如何做到应用程序平滑停止
程序的退出就像关机一样,我们希望关机时平滑关机,保证所有应用程序的数据都保存了。就像现在在写得blog,希望关机的时候能被保存好到草稿箱里。
我们的的Java程序中经常有一种常驻的任务或服务,如消息消费端、服务提供者,我们期望停止也是平滑的不会出现事务执行到一半产生脏数据。
java对这块的支持是通过钩子线程实现。每个Java进程都可以注册钩子线程,钩子线程程在程序退出的前被执行(kill -9强制退出除外)。注册钩子线程代码如下:
- Runtime.getRuntime().addShutdownHook(t);
我们可以在钩子线程里做一些善后数据清理等事情,以保证程序是平滑退出的。
一般服务或框架运行都要考虑其生命周期:
如spring容器的context.stop()方法。
再如线程池ExecutorService的shutdown方法,它会保证不接受新任务,并把未执行完的任务做完。
我们再设计服务的时候也要考虑到停止时的stop方法,以便于退出时由钩子线程调用。
注册了钩子线程后,程序收到退出信号后,会保持程序运行,直到钩子线程执行完毕,才把程序的所有线程停止并退出,下面示例代码可以说明这一点:
- public class ShutDownTest {
- public static void main(String[] args) {
- //注册***个钩子
- Runtime.getRuntime().addShutdownHook(new Thread() {
- public void run() {
- try {
- Thread.currentThread().sleep(5000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("clean task1 completed.");
- }
- });
- //注册第二个钩子
- Runtime.getRuntime().addShutdownHook(new Thread() {
- public void run() {
- try {
- Thread.currentThread().sleep(10000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("clean task2 completed");
- }
- });
- //启动子线程
- new Thread() {
- public void run() {
- while (true) {
- try {
- Thread.currentThread().sleep(1000);
- System.out.println("sub thread is running");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- //程序退出
- System.exit(0);
- }
- }
程序输出:
- sub thread is running
- sub thread is running
- sub thread is running
- sub thread is running
- clean task1 completed.
- sub thread is running
- sub thread is running
- sub thread is running
- sub thread is running
- sub thread is running
- clean task2 completed
注意点:钩子线程里只处理善后,目标是尽可能快的退出且不保证有脏数据。如果钩子线程里做过多事情,或者发生阻塞,那么可能出现kill失效,程序不能退出的情况,这是需要强制退出。
如以下程序会导致kill失效,需要强制退出,因为钩子线程阻塞了:
- public class ShutDownTest {
- public static void main(String[] args) {
- //注册钩子
- Runtime.getRuntime().addShutdownHook(new Thread() {
- public void run() {
- synchronized (ShutdownFileTest.class) {
- try {
- ShutdownFileTest.class.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- });
- //启动子线程
- new Thread() {
- public void run() {
- while (true) {
- try {
- Thread.currentThread().sleep(1000);
- System.out.println("sub thread is running");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- System.exit(0);
- }
- }
程序退出机制选择
触发程序退出的在前面已经提到过,但是为了停止方便、安全和优雅,一般我们推荐几种操控性更强的退出机制。常见的推荐机制有以下几种:
1.kill
在linux里用的比较多,向进程发送退出信号,java进程收到后平滑退出。
2.shutdownfile
系统创建一个shutdown file.并监听shutdown file是否存在。如果发现shutdown file不存在了,那么调用System.exit,将程序退出。
如果期望只有特定的人才能终止该程序,那么你可以给文件设定权限,这样就只有特定的人可以终止程序。
以下代码是个简单的例子:
- import java.io.File;
- import java.io.IOException;
- public class ShutdownFileTest {
- public static void main(String[] args) {
- // 启动子线程
- new Thread() {
- public void run() {
- while (true) {
- try {
- Thread.currentThread().sleep(1000);
- System.out.println("sub thread is running");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- //启动shutdownfile监听线程
- new Thread() {
- public void run() {
- File shutDownFile = new File("a.shutdown");
- // create shut down file
- if (!shutDownFile.exists()) {
- try {
- shutDownFile.createNewFile();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- // watch for file deleted then shutdown
- while (true) {
- try {
- if (shutDownFile.exists()) {
- Thread.currentThread().sleep(1000);
- } else {
- System.exit(0);
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- }
- }
3.打开一个端口,监听端口里的命令,收到命令后调用System.exit。
这个似乎不常见,也比较麻烦。
4.JMX
通过JMX的mbean远程控制来实现。
在这个链接里有看到例子:how-to-stop-java-process-gracefully
原文链接:http://singleant.iteye.com/blog/1441219
【编辑推荐】