You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中如何通过运行中的线程终止接收连接的无限循环?

问题分析与解决方案

你的核心问题其实出在主线程的ss.accept()是阻塞调用——当某个工作线程调用executor.shutdown()后,executor.isShutdown()确实会变为true,但此时主线程可能正卡在ss.accept()上等待新连接,根本没机会检查这个循环条件,所以主循环会一直卡着停不下来。

我们需要解决两个关键点:

  1. 让主线程从阻塞的accept()中唤醒,这样它才能检查isShutdown()条件并退出循环
  2. 确保收到"bye"时,能触发整个服务的优雅停机流程(停止接收新连接 + 终止线程池 + 清理资源)

具体修改方案

1. 调整类结构,让工作线程能触发服务停机

把ServerSocket作为ExecutorHttpd的成员变量,同时让TinyHttpdConnection持有ExecutorHttpd的引用,这样收到"bye"时可以调用服务的停机方法。

2. 完善停机逻辑,关闭ServerSocket唤醒主线程

在shutdown()方法中,先关闭ServerSocket,这样主线程的ss.accept()会抛出IOException,从而跳出循环;然后再处理线程池的优雅关闭。

修改后的完整代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExecutorHttpd {
    private ExecutorService executor = Executors.newFixedThreadPool(3);
    private ServerSocket serverSocket; // 改为成员变量,方便停机时关闭

    public void start(int port) throws IOException {
        serverSocket = new ServerSocket(port);
        try {
            while (!executor.isShutdown()) {
                executor.submit(new TinyHttpdConnection(serverSocket.accept(), this));
            }
        } catch (IOException e) {
            // 当ServerSocket被关闭时,accept()会抛出异常,这里捕获后退出循环
            System.out.println("停止接收新连接,准备关闭服务");
        } finally {
            // 确保ServerSocket被关闭
            if (serverSocket != null && !serverSocket.isClosed()) {
                serverSocket.close();
            }
        }
    }

    public void shutdown() throws InterruptedException {
        System.out.println("开始优雅停机...");
        // 1. 先关闭ServerSocket,停止接收新连接,唤醒主线程的accept()
        try {
            if (serverSocket != null && !serverSocket.isClosed()) {
                serverSocket.close();
            }
        } catch (IOException e) {
            System.out.println("关闭ServerSocket时出错: " + e);
        }

        // 2. 关闭线程池,不再接受新任务
        executor.shutdown();
        // 3. 等待现有任务完成,最多等30秒
        if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
            System.out.println("等待超时,强制终止剩余线程");
            // 4. 强制终止所有正在执行的任务
            executor.shutdownNow();
            // 再等5秒确认终止
            executor.awaitTermination(5, TimeUnit.SECONDS);
        }
        System.out.println("服务已完全关闭");
    }

    public static void main(String argv[]) throws Exception {
        if(argv.length != 1) {
            System.out.println("Wrong number of arguments");
            System.out.println("\tUsage: ExecutorHttpd PORT_NUMBER");
            return;
        }
        int port = Integer.parseInt(argv[0]);
        new ExecutorHttpd().start(port);
    }
}

class TinyHttpdConnection implements Runnable {
    private Socket client;
    private ExecutorHttpd server; // 持有服务实例,用于触发停机

    TinyHttpdConnection(Socket client, ExecutorHttpd server) throws SocketException {
        this.client = client;
        this.server = server;
    }

    public void run() {
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
            OutputStream out = client.getOutputStream();
            String request = in.readLine();
            System.out.println("Request: " + request);

            if("bye".equals(request)) {
                System.out.println("收到终止指令,通知服务停机");
                // 触发服务的停机流程
                server.shutdown();
            }

            byte[] data = "hello".getBytes();
            out.write(data, 0, data.length);
            out.flush();
        } catch (IOException e) {
            System.out.println("I/O error " + e);
        } finally {
            // 确保客户端连接被关闭
            try {
                if (client != null && !client.isClosed()) {
                    client.close();
                }
            } catch (IOException e) {
                System.out.println("关闭客户端连接时出错: " + e);
            }
        }
    }
}

关键改动说明

  • ServerSocket改为成员变量:这样在停机时可以主动关闭它,打破主线程的accept()阻塞
  • TinyHttpdConnection持有服务实例:收到"bye"时直接调用server.shutdown()触发整个停机流程
  • 主线程捕获accept()异常:当ServerSocket被关闭时,accept()会抛出IOException,主线程捕获后退出循环
  • 完善停机步骤:先停止接收新连接,再逐步关闭线程池,确保优雅停机

这样修改后,当你用telnet发送"bye"时,工作线程会触发服务的shutdown流程:关闭ServerSocket唤醒主线程,主线程检查到executor.isShutdown()为true(或者因为异常跳出循环),然后线程池会优雅关闭现有任务,最终整个服务正常退出。

内容的提问来源于stack exchange,提问作者albertgumi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 10:07:45