2008-10-10 164 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。