2008-10-10 98 views
82

如何在Linux和Windows中优雅地停止Java进程?如何优雅地停止java进程?

Runtime.getRuntime().addShutdownHook何时被调用,什么时候不调用?

终结者怎么样,他们在这里帮助吗?

我可以从shell发送某种信号给Java进程吗?

我在寻找最好的便携式解决方案。

+0

你应该定义你的优雅是什么意思。 (please) – 2008-10-10 13:23:03

+6

我假设他们的意思是他们希望有机会在程序被终止之前清理资源,释放,锁定并刷新任何持久数据到磁盘。 – 2008-10-10 13:25:34

回答

72

关闭挂钩在虚拟机未被强行杀死的所有情况下执行。因此,如果您要发出“标准”杀死(kill命令中的SIGTERM),那么它们将执行。同样,他们将在致电System.exit(int)后执行。

但是一个硬杀(kill -9kill -SIGKILL),那么他们将不会执行。同样的(显然),如果你从计算机中取出电源,将它放入沸腾的熔岩桶中,或者用大锤击碎CPU,它们将不会执行。不过,你可能已经知道了。

终结器真的应该运行,但最好不要依赖于关闭清理,而是依靠你的关闭钩子干净地停止事情。和往常一样,要小心死锁(我已经看到太多的关机挂钩挂在整个过程中)!

2

相似问题Here

Java中的终结器是不好的。它们为垃圾收集增加了很多开销。尽可能避免使用它们。

shutdownHook只会在VM关闭时被调用。我认为它可以做你想做的事。

+1

因此在所有情况下都会调用shutdownHook?如果从shell中“杀死”进程会怎么样? – Ma99uS 2008-10-10 13:29:12

0

信号可以“杀”(人杀死可用的信号)来完成,你需要的进程ID做到这一点。 (ps ax | grep java)或类似的东西,或者当进程被创建时存储进程ID(这在大多数linux启动文件中使用,参见/etc/init.d)

便携信令可以通过在您的Java应用程序中集成SocketServer。这并不困难,并让您自由发送任何您想要的命令。

如果您的意思是最终条款而不是终结者;当System.exit()被调用时它们不会被扩展。 终结器应该可以工作,但不应该做任何更重要的事情,但应该打印调试声明。他们很危险。

46

好吧,毕竟我已经选择与“Java监视和管理”
Overview is here
这允许你从另外一个相对简单的方式控制一个应用程序的工作准备。您可以从脚本中调用控制应用程序,以便在杀死它之前正常停止受控应用程序。

这里是简化的代码:

受控应用:
与如下因素VM参数运行:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote .port = 9999
-Dcom.sun.management.jmxremote.authenticate =假
-Dcom.sun.management.jmxremote.ssl =假

//ThreadMonitorMBean.java 
public interface ThreadMonitorMBean 
{ 
String getName(); 
void start(); 
void stop(); 
boolean isRunning(); 
} 

// ThreadMonitor.java 
public class ThreadMonitor implements ThreadMonitorMBean 
{ 
private Thread m_thrd = null; 

public ThreadMonitor(Thread thrd) 
{ 
    m_thrd = thrd; 
} 

@Override 
public String getName() 
{ 
    return "JMX Controlled App"; 
} 

@Override 
public void start() 
{ 
    // TODO: start application here 
    System.out.println("remote start called"); 
} 

@Override 
public void stop() 
{ 
    // TODO: stop application here 
    System.out.println("remote stop called"); 

    m_thrd.interrupt(); 
} 

public boolean isRunning() 
{ 
    return Thread.currentThread().isAlive(); 
} 

public static void main(String[] args) 
{ 
    try 
    { 
     System.out.println("JMX started"); 

     ThreadMonitorMBean monitor = new ThreadMonitor(Thread.currentThread()); 

     MBeanServer server = ManagementFactory.getPlatformMBeanServer(); 

     ObjectName name = new ObjectName("com.example:type=ThreadMonitor"); 

     server.registerMBean(monitor, name); 

     while(!Thread.interrupted()) 
     { 
      // loop until interrupted 
      System.out.println("."); 
      try 
      { 
       Thread.sleep(1000); 
      } 
      catch(InterruptedException ex) 
      { 
       Thread.currentThread().interrupt(); 
      } 
     } 
    } 
    catch(Exception e) 
    { 
     e.printStackTrace(); 
    } 
    finally 
    { 
     // TODO: some final clean up could be here also 
     System.out.println("JMX stopped"); 
    } 
} 
} 

控制应用:
停止开始作为命令行参数

public class ThreadMonitorConsole 
{ 

public static void main(String[] args) 
{ 
    try 
    { 
     // connecting to JMX 
     System.out.println("Connect to JMX service."); 
     JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi"); 
     JMXConnector jmxc = JMXConnectorFactory.connect(url, null); 
     MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); 

     // Construct proxy for the the MBean object 
     ObjectName mbeanName = new ObjectName("com.example:type=ThreadMonitor"); 
     ThreadMonitorMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName, ThreadMonitorMBean.class, true); 

     System.out.println("Connected to: "+mbeanProxy.getName()+", the app is "+(mbeanProxy.isRunning() ? "" : "not ")+"running"); 

     // parse command line arguments 
     if(args[0].equalsIgnoreCase("start")) 
     { 
      System.out.println("Invoke \"start\" method"); 
      mbeanProxy.start(); 
     } 
     else if(args[0].equalsIgnoreCase("stop")) 
     { 
      System.out.println("Invoke \"stop\" method"); 
      mbeanProxy.stop(); 
     } 

     // clean up and exit 
     jmxc.close(); 
     System.out.println("Done.");  
    } 
    catch(Exception e) 
    { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
} 
} 


就是这样运行它。 :-)

7

另一种方式:您的应用程序可以打开一个服务器socet并等待一个信息到达它。例如一个带有“魔术”字符的字符串:)然后做出关闭:System.exit()的反应。您可以使用外部应用程序(如telnet)将这些信息发送到socke。