Java中如何通过运行中的线程终止接收连接的无限循环?
问题分析与解决方案
你的核心问题其实出在主线程的ss.accept()是阻塞调用——当某个工作线程调用executor.shutdown()后,executor.isShutdown()确实会变为true,但此时主线程可能正卡在ss.accept()上等待新连接,根本没机会检查这个循环条件,所以主循环会一直卡着停不下来。
我们需要解决两个关键点:
- 让主线程从阻塞的
accept()中唤醒,这样它才能检查isShutdown()条件并退出循环 - 确保收到"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
相关产品推荐
相关产品推荐

