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

Java多线程客户端Socket CPU占用100%问题求助

问题分析与解决方案

先给你拆解下代码导致CPU爆满、接收速度下降的核心原因:

  • 忙等待直接吃光CPU:你外层的while(port == 7002)这类循环里,一旦is.ready()返回false(暂时没数据),循环会立刻重复执行,线程相当于一直在空转,完全不给CPU释放资源的机会,很快就会占满100%,后续数据处理自然被拖慢。
  • 逐字符读取效率极低:每次只读1个字符,频繁的IO系统调用会带来极大的性能开销,在每秒2000条的高并发场景下,这种方式的瓶颈会被无限放大。
  • 重复代码冗余:三个端口的处理逻辑几乎完全一致,不仅难维护,还容易引入重复的bug。

下面是针对性的修复方案,附修改后的完整代码和关键改动说明:

修改后的代码

import java.io.*;
import java.net.*;

public class Client implements Runnable {
    private Socket clientSocket = null;
    private BufferedReader is = null;
    private int port;
    // 保留未完成的JSON片段,处理跨缓冲的不完整数据
    private StringBuffer incompleteJson = new StringBuffer();

    public Client(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        new Thread(new Client(7002)).start();
        new Thread(new Client(7003)).start();
        new Thread(new Client(7004)).start();
    }

    @Override
    public void run() {
        System.out.println("Thread started for port: " + port);
        try {
            clientSocket = new Socket("192.168.2.156", port);
            // 初始化更大的缓冲区,提升读取效率
            is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()), 8192);
            // 统一处理逻辑,无需分端口写重复代码
            processIncomingData();
        } catch (IOException e) {
            System.err.println("Error on port " + port + ": " + e.getMessage());
        } finally {
            // 确保资源被正确关闭,避免泄漏
            try {
                if (is != null) is.close();
                if (clientSocket != null) clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void processIncomingData() throws IOException {
        char[] buffer = new char[1024]; // 批量读取的缓冲数组
        int bytesRead;
        // 阻塞式读取:没有数据时线程会挂起,不占用CPU
        while ((bytesRead = is.read(buffer)) != -1) {
            String chunk = new String(buffer, 0, bytesRead);
            incompleteJson.append(chunk);
            // 解析所有能找到的完整JSON对象
            parseCompleteJson();
        }
    }

    private void parseCompleteJson() {
        String jsonContent = incompleteJson.toString();
        int startIndex = jsonContent.indexOf('{');
        // 循环提取所有完整的JSON(从{到})
        while (startIndex != -1) {
            int endIndex = jsonContent.indexOf('}', startIndex);
            if (endIndex == -1) {
                // 未找到结束的},保留剩余的不完整片段,下次继续处理
                incompleteJson = new StringBuffer(jsonContent.substring(startIndex));
                break;
            }
            // 提取完整JSON并输出
            String completeJson = jsonContent.substring(startIndex, endIndex + 1);
            System.out.println("Received data from port " + port + ": " + completeJson);
            // 更新剩余内容,继续查找下一个JSON
            jsonContent = jsonContent.substring(endIndex + 1);
            startIndex = jsonContent.indexOf('{');
        }
        // 处理完后保留未完成的部分
        if (startIndex == -1) {
            incompleteJson = new StringBuffer(jsonContent);
        }
    }
}

关键改动说明

  1. 彻底消除忙等待:去掉了is.ready()的循环,改用is.read(buffer)的阻塞式读取。当没有数据时,线程会进入操作系统的等待队列,不占用CPU资源,这是解决CPU100%问题的核心。
  2. 批量读取提升效率:用1024大小的char数组批量读取数据,大幅减少IO调用次数,完美适配高并发的接收场景。
  3. 重构重复逻辑:把三个端口的处理逻辑抽成通用方法,代码更简洁,后续维护也更方便。
  4. 处理跨缓冲的JSON:用incompleteJson保留未完成的JSON片段,避免因为一个JSON被拆分成多个缓冲块而导致解析失败。
  5. 资源安全关闭:添加finally块确保Socket和流资源被正确关闭,避免长期运行的资源泄漏问题。

额外小建议:

  • 如果JSON数据结构复杂,建议用专业的JSON解析库(比如Jackson)替代手动解析,可靠性和效率都会更高。
  • 高并发下System.out.println是同步操作,可能成为新的性能瓶颈,可以考虑用异步日志框架(比如SLF4J+Logback)替代。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 19:12:28