首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 防止程序多實例運行:文件鎖與 Socket 鎖方案實戰

     編輯:Java 相關知識

問題背景

原文提供的是 C++/MFC 互斥體代碼,Java 中無原生互斥體 API,但可通過文件鎖或本地 Socket 端口佔用實現單實例運行。

方案一:文件鎖 —— FileChannel.lock()

利用操作系統文件鎖機制,進程退出自動釋放:

public class SingleInstanceLock {
    private static FileChannel channel;
    private static FileLock lock;
    
    public static boolean tryLock(String lockFilePath) {
        try {
            File file = new File(lockFilePath);
            channel = new RandomAccessFile(file, "rw").getChannel();
            lock = channel.tryLock(); // 非阻塞嘗試
            if (lock == null) return false; // 已被鎖定
            // 添加關閉鉤子釋放(雖然 JVM 退出自動釋放)
            Runtime.getRuntime().addShutdownHook(new Thread(() -> release()));
            return true;
        } catch (IOException e) {
            return false;
        }
    }
    private static void release() {
        try { if (lock != null) lock.release(); if (channel != null) channel.close(); } 
        catch (IOException ignored) {}
    }
}

// 主程序入口
public static void main(String[] args) {
    if (!SingleInstanceLock.tryLock(System.getProperty("java.io.tmpdir") + "/myapp.lock")) {
        System.out.println("程序已在運行!");
        // 可選:發送激活信號給已有進程(需額外 IPC 機制)
        return;
    }
    // 正常啟動業務邏輯...
}

優點:跨平台、語義清晰、進程崩潰自動釋放。
缺點:網絡文件系統 (NFS) 上鎖行為不可靠。

方案二:本地 Socket 端口佔用

綁定 localhost 高位端口,佔用成功即獲取鎖:

public class SingleInstanceSocket {
    private static ServerSocket serverSocket;
    private static final int PORT = 54321; // 固定端口
    
    public static boolean tryLock() {
        try {
            serverSocket = new ServerSocket(PORT, 1, InetAddress.getByName("127.0.0.1"));
            // 保持 ServerSocket 存活直到程序結束
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                try { serverSocket.close(); } catch (IOException ignored) {}
            }));
            return true;
        } catch (IOException e) {
            return false; // 端口被佔用
        }
    }
}

優點:極簡、可擴展為進程間通訊(IPC)通道。
缺點:端口衝突風險(建議用 >49151 動態端口)、防火牆可能干擾。

進階:單實例 + 參數傳遞/窗口激活

若需將二次啟動的參數傳給主實例、或激活主窗口:

  1. 主實例啟動時啟動 ServerSocket.accept() 循環監聽
  2. 二次啟動客戶端連接 Socket,發送參數/激活指令
  3. 主實例接收後處理(如 Platform.runLater(() -> stage.toFront()) JavaFX)

總結建議

  • 首選文件鎖:語義明確、穩定可靠
  • 需 IPC 通訊選 **Socket 鎖**
  • 避免使用 createTempFile 刪除檢查法(競態條件不可靠)
  • GUI 程序結合平台 API(如 Windows JNI、macOS NSRunningApplication)可實現原生級體驗
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved