设计Java应用程序的平滑停止

开发 后端
程序的退出就像关机一样,我们希望关机时平滑关机,保证所有应用程序的数据都保存了。就像现在在写得blog,希望关机的时候能被保存好到草稿箱里。

Java应用程序退出的触发机制有:

  1. 自动结束:应用没有存活线程或只有后台线程时;
  2. System.exit(0);
  3. kill 或 ctrl+C;
  4. kill -9 强制退出;

如何做到应用程序平滑停止

程序的退出就像关机一样,我们希望关机时平滑关机,保证所有应用程序的数据都保存了。就像现在在写得blog,希望关机的时候能被保存好到草稿箱里。

我们的的Java程序中经常有一种常驻的任务或服务,如消息消费端、服务提供者,我们期望停止也是平滑的不会出现事务执行到一半产生脏数据。

 

java对这块的支持是通过钩子线程实现。每个Java进程都可以注册钩子线程,钩子线程程在程序退出的前被执行(kill -9强制退出除外)。注册钩子线程代码如下:

  1. Runtime.getRuntime().addShutdownHook(t); 

我们可以在钩子线程里做一些善后数据清理等事情,以保证程序是平滑退出的。

一般服务或框架运行都要考虑其生命周期:

如spring容器的context.stop()方法。

再如线程池ExecutorService的shutdown方法,它会保证不接受新任务,并把未执行完的任务做完。

 

我们再设计服务的时候也要考虑到停止时的stop方法,以便于退出时由钩子线程调用。

注册了钩子线程后,程序收到退出信号后,会保持程序运行,直到钩子线程执行完毕,才把程序的所有线程停止并退出,下面示例代码可以说明这一点:

  1. public class ShutDownTest {  
  2.  
  3.     public static void main(String[] args) {  
  4.         //注册***个钩子  
  5.         Runtime.getRuntime().addShutdownHook(new Thread() {  
  6.  
  7.             public void run() {  
  8.                 try {  
  9.                     Thread.currentThread().sleep(5000);  
  10.                 } catch (InterruptedException e) {  
  11.                     e.printStackTrace();  
  12.                 }  
  13.                 System.out.println("clean task1 completed.");  
  14.             }  
  15.         });  
  16.         //注册第二个钩子  
  17.         Runtime.getRuntime().addShutdownHook(new Thread() {  
  18.  
  19.             public void run() {  
  20.                 try {  
  21.                     Thread.currentThread().sleep(10000);  
  22.                 } catch (InterruptedException e) {  
  23.                     e.printStackTrace();  
  24.                 }  
  25.                 System.out.println("clean task2 completed");  
  26.             }  
  27.         });  
  28.         //启动子线程  
  29.         new Thread() {  
  30.  
  31.             public void run() {  
  32.                 while (true) {  
  33.                     try {  
  34.                         Thread.currentThread().sleep(1000);  
  35.                         System.out.println("sub thread is running");  
  36.                     } catch (InterruptedException e) {  
  37.                         e.printStackTrace();  
  38.                     }  
  39.                 }  
  40.             }  
  41.         }.start();  
  42.         //程序退出  
  43.         System.exit(0);  
  44.     }  
  45.  

程序输出:

  1. sub thread is running  
  2. sub thread is running  
  3. sub thread is running  
  4. sub thread is running  
  5. clean task1 completed.  
  6. sub thread is running  
  7. sub thread is running  
  8. sub thread is running  
  9. sub thread is running  
  10. sub thread is running  
  11. clean task2 completed 

注意点:钩子线程里只处理善后,目标是尽可能快的退出且不保证有脏数据。如果钩子线程里做过多事情,或者发生阻塞,那么可能出现kill失效,程序不能退出的情况,这是需要强制退出。

如以下程序会导致kill失效,需要强制退出,因为钩子线程阻塞了:

  1. public class ShutDownTest {  
  2.  
  3.     public static void main(String[] args) {  
  4.         //注册钩子  
  5.         Runtime.getRuntime().addShutdownHook(new Thread() {  
  6.             public void run() {  
  7.                 synchronized (ShutdownFileTest.class) {  
  8.                     try {  
  9.                         ShutdownFileTest.class.wait();  
  10.                     } catch (InterruptedException e) {  
  11.                         e.printStackTrace();  
  12.                     }  
  13.                 }  
  14.             }  
  15.         });  
  16.         //启动子线程  
  17.         new Thread() {  
  18.             public void run() {  
  19.                 while (true) {  
  20.                     try {  
  21.                         Thread.currentThread().sleep(1000);  
  22.                         System.out.println("sub thread is running");  
  23.                     } catch (InterruptedException e) {  
  24.                         e.printStackTrace();  
  25.                     }  
  26.                 }  
  27.             }  
  28.         }.start();  
  29.        System.exit(0);  
  30.        }  
  31.  

程序退出机制选择

触发程序退出的在前面已经提到过,但是为了停止方便、安全和优雅,一般我们推荐几种操控性更强的退出机制。常见的推荐机制有以下几种:

1.kill

在linux里用的比较多,向进程发送退出信号,java进程收到后平滑退出。

2.shutdownfile

系统创建一个shutdown file.并监听shutdown file是否存在。如果发现shutdown file不存在了,那么调用System.exit,将程序退出。

如果期望只有特定的人才能终止该程序,那么你可以给文件设定权限,这样就只有特定的人可以终止程序。

以下代码是个简单的例子:

  1. import java.io.File;  
  2. import java.io.IOException;  
  3.  
  4. public class ShutdownFileTest {  
  5.  
  6.     public static void main(String[] args) {  
  7.         // 启动子线程  
  8.         new Thread() {  
  9.  
  10.             public void run() {  
  11.                 while (true) {  
  12.                     try {  
  13.                         Thread.currentThread().sleep(1000);  
  14.                         System.out.println("sub thread is running");  
  15.                     } catch (InterruptedException e) {  
  16.                         e.printStackTrace();  
  17.                     }  
  18.                 }  
  19.             }  
  20.         }.start();  
  21.           
  22.         //启动shutdownfile监听线程  
  23.         new Thread() {  
  24.  
  25.             public void run() {  
  26.                 File shutDownFile = new File("a.shutdown");  
  27.                 // create shut down file  
  28.                 if (!shutDownFile.exists()) {  
  29.                     try {  
  30.                         shutDownFile.createNewFile();  
  31.                     } catch (IOException e) {  
  32.                         e.printStackTrace();  
  33.                     }  
  34.                 }  
  35.                 // watch for file deleted then shutdown   
  36.                 while (true) {  
  37.                     try {  
  38.                         if (shutDownFile.exists()) {  
  39.                             Thread.currentThread().sleep(1000);  
  40.                         } else {  
  41.                             System.exit(0);  
  42.                         }  
  43.                     } catch (InterruptedException e) {  
  44.                         e.printStackTrace();  
  45.                     }  
  46.                 }  
  47.             }  
  48.         }.start();  
  49.     }  
  50.  

3.打开一个端口,监听端口里的命令,收到命令后调用System.exit。

这个似乎不常见,也比较麻烦。

4.JMX

通过JMX的mbean远程控制来实现。

在这个链接里有看到例子:how-to-stop-java-process-gracefully

原文链接:http://singleant.iteye.com/blog/1441219

【编辑推荐】

  1. 深入Java探索:Java内存区域
  2. 同一段程序在Java和C中的不同结果
  3. Java中Runnable和Thread的区别
  4. Java对存储过程的调用方法
  5. Java初学者都必须理解的六大问题
责任编辑:林师授 来源: singleant的博客
相关推荐

2023-08-14 08:03:25

前端应用程序Typescript

2020-12-28 14:40:47

云计算云应用SaaS

2011-10-25 10:24:03

Windows Pho

2010-06-12 16:59:45

UML设计

2011-04-11 14:45:41

性能设计官方文档Android

2009-09-03 08:46:55

UML类图Java

2015-01-06 09:59:59

云应用程序Java开发SQL

2012-11-15 10:18:11

IBMdw

2012-02-15 14:39:55

GNOME 3

2022-05-04 23:08:36

标准Go应用程序

2010-03-04 10:11:17

Android手机系统

2010-08-12 15:59:23

Flex应用程序

2012-04-04 22:10:14

UI

2020-03-27 09:20:00

单页应用程序网页设计SPAs

2011-04-01 11:01:02

应用程序BlackBerryJava

2009-09-27 10:37:01

Java应用程序Hibernate

2012-08-01 14:12:45

IBMdW

2010-09-06 14:33:46

AndroidJava

2012-06-07 09:15:14

ibmdw

2012-05-29 10:04:08

点赞
收藏

51CTO技术栈公众号