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

本地Java Socket服务端线程数下降引发连接超时问题求助

Java Socket 连接超时与活跃线程数下降问题分析与解决

问题根源分析

1. 客户端未释放Socket资源,导致本地端口耗尽

客户端每次发起请求后,没有关闭Socket连接。每个TCP连接会占用客户端一个本地端口,当50个线程累计创建大量未关闭的Socket后,系统可用的本地端口被耗尽,无法发起新的TCP连接,最终触发java.net.ConnectException: Operation timed out异常。

2. 服务端活跃线程数下降的原因

当客户端因端口耗尽停止发送新请求后,服务端线程池中的任务会逐步处理完所有已接收的连接,线程回到空闲状态,因此ActiveCount统计的活跃线程数逐渐下降。最终服务端的ServerSocket.accept()会阻塞等待新连接,此时线程池中无正在执行的任务,若你看到剩余1个活跃线程,大概率是统计逻辑的误差(比如ActiveCount非原子实现导致的计数偏差)。

解决方案

1. 客户端强制关闭Socket资源

在客户端的请求逻辑中,必须确保每次请求完成后释放Socket,避免端口泄漏。推荐使用try-with-resources语法(Java 7+),自动实现资源关闭:

public class SocketClientMultithreaded {
    static CyclicBarrier barrier; 
    
    public static void main(String[] args){
        String hostName;
        int port;
        int numOfThreads = 50;

        hostName= "localhost";
        port = 12031;
        barrier = new CyclicBarrier(numOfThreads);

        for (int i = 0; i < numOfThreads; i++) {
            Runnable runnable = () -> {
                for (int j = 0; j < 1000; j++) {
                    // try-with-resources自动关闭Socket、流资源
                    try (Socket s = new Socket(hostName, port);
                         PrintWriter out = new PrintWriter(s.getOutputStream(), true);
                         BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()))) {
                        out.println("Client ID is " +  Thread.currentThread().getId());
                        System.out.println(in.readLine());
                    } catch (UnknownHostException e) {
                        System.err.println("Don't know about host " + hostName);
                        break;
                    } catch (IOException e) {
                        System.err.println("I/O connection exception: " + e);
                        break;
                    }
                }
                try {
                    System.out.println("Thread waiting at barrier");
                    barrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    Logger.getLogger(SocketClientMultithreaded.class.getName()).log(Level.SEVERE, null, e);
                }
            };
            new Thread(runnable).start();
        }
    }
}

2. 修复服务端统计的线程安全性

确保ActiveCount使用原子类实现,避免多线程下的计数错误:

class ActiveCount {
    private final AtomicInteger activeThreads = new AtomicInteger(0);

    public void incrementCount() {
        activeThreads.incrementAndGet();
    }

    public void decrementCount() {
        activeThreads.decrementAndGet();
    }

    public int getCount() {
        return activeThreads.get();
    }
}

3. 可选:扩大系统端口范围(临时方案)

若需要支持更大并发量,可临时调整系统的本地端口范围(以Linux为例):

# 查看当前端口范围
sysctl net.ipv4.ip_local_port_range
# 修改为更大的端口范围
sysctl -w net.ipv4.ip_local_port_range="1024 65535"

注意:此方案仅为临时缓解,根本解决仍需依赖客户端的资源释放逻辑。

验证效果

修改后重新运行,会观察到:

  • 服务端活跃线程数稳定维持在20(直到所有请求处理完成)
  • 客户端无端口耗尽导致的超时异常
  • 请求处理完毕后,服务端活跃线程数逐步降至0

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 01:13:28