現象:.bat 設定環境變數後啟動 java -jar app.jar,會出現三個程序:cmd.exe、java.exe (JVM)、conhost.exe。在工作管理員結束 cmd.exe 和 conhost.exe 後,JVM 仍在背景運行,使用者以為程式關閉而重複啟動,造成衝突。
cmd.exe 是批次檔解譯器,負責啟動 JVM 進程。java.exe) 一旦啟動,即成為獨立進程,**不再依賴父進程 cmd.exe 存活** (Windows 非 Unix fork 模式)。conhost.exe 是主控台視窗宿主,僅負責輸入輸出轉發。在 Java 程式啟動時建立檔案鎖或 Socket 埠鎖:
public class SingleInstance {
private static FileLock lock;
public static boolean tryLock(String lockFile) {
try {
RandomAccessFile raf = new RandomAccessFile(lockFile, "rw");
lock = raf.getChannel().tryLock();
return lock != null;
} catch (IOException e) { return false; }
}
public static void main(String[] args) {
if (!tryLock(System.getProperty("java.io.tmpdir") + "/myapp.lock")) {
System.err.println("程式已在運行");
System.exit(1);
}
// 正常啟動邏輯
}
}
start /b 或 wscript 隱藏主控台@echo off
set JAVA_HOME=...
start /b javaw -jar app.jar // javaw 無主控台視窗
// 或
wscript //b //nologo //e:jscript // "%~f0" // 更徹底隱藏
@echo off
tasklist | findstr /i "java.exe" | findstr "app.jar" >nul
if %errorlevel% equ 0 (
echo 已在運行
timeout /t 3
exit /b
)
start /b javaw -jar app.jar
javaw 或服務包裝器 (Procrun、NSSM、systemd)**,不要依賴 .bat + cmd 視窗。pause 讓使用者看到錯誤訊息,避免誤以為閃退而重啟。理解「批次檔只是啟動器,JVM 是獨立進程」,再配合單一實例機制,即可徹底解決「關視窗程式還在跑、使用者重啟衝突」的問題。