生產環境常需守護進程:當目標 Java 程序異常退出或需定時重啟時,由監控程序拉起。本文提供基於數據庫信號量 + Runtime.exec() 的雙程序協作方案,含完整代碼與關鍵細節處理。
System.exit(0)restart:單行單列 status INT,三態:0=運行中、1=請求關閉、2=已關閉待啟動public class DBTool {
private Connection conn;
private Statement stmt;
public DBTool() throws Exception {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String url = "jdbc:sqlserver://localhost:1433;databaseName=tianjincollect;user=sa;password=123456";
conn = DriverManager.getConnection(url);
stmt = conn.createStatement();
}
public ResultSet executeQuery(String sql) throws SQLException { return stmt.executeQuery(sql); }
public int executeUpdate(String sql) throws SQLException { return stmt.executeUpdate(sql); }
public void close() throws SQLException {
if (stmt != null) stmt.close();
if (conn != null) conn.close();
}
}
ReStartpublic class ReStart implements Runnable {
private final DBTool db = new DBTool();
private final String sqlSel = "SELECT status FROM restart";
private final String sqlReset = "UPDATE restart SET status = 0";
@Override public void run() {
// 初始化狀態
try { db.executeUpdate(sqlReset); } catch (SQLException e) { e.printStackTrace(); }
while (true) {
try {
ResultSet rs = db.executeQuery(sqlSel);
if (rs.next()) {
int status = rs.getInt("status");
System.out.println("檢測狀態: " + status);
if (status == 2) { // 已關閉,需啟動
System.out.println("啟動目標程序...");
db.executeUpdate(sqlReset); // 先重置狀態防止重複啟動
// 關鍵:用 cmd /c start 調用批處理,批處理中啟動 java -jar xxx.jar
Process p = Runtime.getRuntime().exec("cmd /c start E:\\Bats\\MainThread.bat");
p.waitFor();
}
}
} catch (Exception e) { e.printStackTrace(); }
sleep(5000);
}
}
private void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException ignored) {} }
public static void main(String[] args) { new ReStart().run(); }
}
ExitMainpublic class ExitMain implements Runnable {
private final DBTool db = new DBTool();
private final String sqlSel = "SELECT status FROM restart";
private final String sqlSet2 = "UPDATE restart SET status = 2";
@Override public void run() {
while (true) {
try {
ResultSet rs = db.executeQuery(sqlSel);
if (rs.next() && rs.getInt("status") == 1) { // 收到關閉指令
System.out.println("收到關閉指令,準備退出...");
db.executeUpdate(sqlSet2); // 標記已關閉
System.exit(0); // 雅退出
}
} catch (Exception e) { e.printStackTrace(); }
sleep(5000);
}
}
private void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException ignored) {} }
public static void main(String[] args) { new ExitMain().run(); }
}
MainThread.bat@echo off java -Xms256m -Xmx512m -jar target-program.jar rem 程序結束後窗口保持,便於查看日志 pause
System.exit() 後,OS 回收端口需時間。程序 A 啟動前 Thread.sleep(5000) 或批處理中 ping -n 5 127.0.0.1 >nul 延遲jps -l 或端口探活確認程序 B 真實存活SIGTERM (Linux) / Ctrl+C (Windows) 執行清理邏輯,而非硬性 System.exit()/actuator/health 暴露健康狀態,Prometheus/Grafana 告警觸發重啟本方案適合無法使用容器編排、需快速落地的輕量級守護場景;核心思想是「外部監控 + 內部協作 + 狀態持久化」。