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

Java TCP 網絡編程進階:守護線程定時掃描釋放殭屍 Socket

     編輯:Java 綜合教學

痛點:Socket 輸入/輸出流關閉後,Socket 對象未釋放導致文件描述符洩漏

在前例中,客戶端發送 byebye 關閉流後,服務端調用 socket.close() 釋放資源。但若客戶端異常斷網、進程崩潰、強制殺進程,流關閉而 Socket 未關閉,長期積累導致服務端「Too many open files」崩潰。

解決方案:後台守護線程周期性掃描

設計 ServerScanThread 守護線程,維護一個 List<Socket>,定期檢查 socket.isInputShutdown() && socket.isOutputShutdown(),雙流均關閉則調用 socket.close() 並從列表移除。

核心代碼:ServerScanThread

public class ServerScanThread extends Thread {
    public static List<Socket> socketList = new LinkedList<>();

    public ServerScanThread() {
        setDaemon(true); // 守護線程,主線程退出自動終了
        start();
    }

    @Override
    public void run() {
        while (true) {
            // 1. 空列表休眠 10s
            while (socketList == null || socketList.isEmpty()) {
                try { Thread.sleep(10_000); } catch (InterruptedException ignored) {}
            }

            // 2. 掃描列表
            for (int i = 0; i < socketList.size(); i++) {
                Socket socket = socketList.get(i);
                if (socket.isInputShutdown() && socket.isOutputShutdown()) {
                    try {
                        socket.close();
                        socketList.remove(i--); // LinkedList 移除後索引前移,i-- 防止漏掃
                        System.out.println("已清理 1 個殭屍 Socket");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            // 3. 掃描間隔 5s
            try { Thread.sleep(5_000); } catch (InterruptedException ignored) {}
        }
    }
}

服務端接入:Server1

public class Server1 {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(9880);
        new ServerScanThread(); // 啟動掃描線程

        while (true) {
            Socket client = server.accept();
            ServerScanThread.socketList.add(client); // 納入管理
            System.out.println("客戶端接入: " + client.getInetAddress());

            // 業務處理線程
            new WriterThread("服務端", client).start();
            new ReaderThread("服務端", client).start();
        }
    }
}

運行現象驗證

場景掃描線程行為控制台輸出
無客戶端外層 while 休眠 10s 一次打印這會兒集合裡沒有socket,我先睡覺10s
客戶端連接但未關閉流進入 for 循環掃描,條件不滿足無額外輸出,CPU 低佔用
客戶端雙向關閉流檢測到雙流關閉,執行 close() 並移除有一個 socket 被關閉了

無客戶端時掃描線程休眠

客戶端連接未關流時掃描

檢測到雙流關閉並清理 Socket

生產環境改進建議

  1. 使用 NIO/Netty:Selector 事件驅動模型天然解決大量連接管理問題,無需輪詢。
  2. 心跳機制:應用層定義心跳包,超過 N 次無響應主動關閉,比 TCP 層面更可控。
  3. 連接池與生命週期管理:引入 HikariCP 等成熟組件,統一管理資源獲取釋放。
  4. 操作系統層面:調大 ulimit -n、開啟 tcp_tw_reuse、tcp_tw_recycle(內核參數謹慎調優)。

守護線程掃描是 BIO 模型下的務實補丁,理解其原理有助於遷移到 NIO/Netty 時做出正確架構決策。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved