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

Java MVC客户端-服务端计算器点击计算后GUI冻结问题求助

问题根因

  • Swing GUI的所有事件回调都运行在*事件调度线程(EDT)*中,你直接在按钮点击的ActionListener里执行同步Socket IO、阻塞式读服务端响应的操作,EDT被占住无法处理界面绘制、用户交互等事件,直接出现冻结。
  • 服务端Controller逻辑存在bug:客户端第一次建立连接时,服务端Controller会先读取一行请求,再把Socket交给Application线程处理,而Application的menu方法又会再次调用readLine()读取下一行请求,导致第一次点击计算的请求被服务端吞掉,客户端一直等不到响应、死循环阻塞,进一步加重了界面冻结问题。
  • 客户端readFromServer的逻辑冗余,服务端返回的结果已经携带\0结束标记,不需要循环拼接内容,且readLine本身是阻塞方法,未读到数据会一直等待,不会继续执行后续逻辑。

修复步骤

1. 改造客户端计算逻辑,异步执行网络请求避免阻塞EDT

把Socket通信逻辑放到独立工作线程执行,拿到结果后再切回EDT更新UI,修改ClientController的calculate方法:

public void calculate() {
    // 从UI取数必须在EDT执行,先拿到参数
    final String firstNum = Integer.toString(view.getFirstNumber());
    final String secNum = Integer.toString(view.getSecondNumber());
    // 开工作线程执行阻塞式网络IO
    new Thread(() -> {
        socketOut.println("1" + firstNum + "," + secNum);
        final int result = Integer.parseInt(readFromServer());
        // 更新UI必须切回EDT执行
        SwingUtilities.invokeLater(() -> {
            view.setTheSolution(result);
        });
    }).start();
}

2. 简化客户端读服务端响应的逻辑

private String readFromServer() {
    try {
        String s = socketIn.readLine();
        // 直接移除结束标记返回即可
        return s.replace("\0", "");
    } catch (IOException e) {
        System.out.println("Error in reading from server.");
        e.printStackTrace();
        return "0";
    }
}

3. 修复服务端Controller逻辑,删除提前读请求的冗余代码

修改ServerController的communicate方法:

public void communicate() {
    while(true) {
        try {
            Socket socket = serverSocket.accept();
            BufferedReader socketIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter socketOut = new PrintWriter(socket.getOutputStream(), true);
            // 删除下面四行冗余代码,请求读取交给Application线程处理
            // String request = socketIn.readLine();
            // String[] args = request.split(",");
            // int firstNum = Integer.parseInt(args[0]);
            // int secNum = Integer.parseInt(args[1]);
            
            Application app = new Application(calc, socketOut, socketIn);
            pool.execute(app);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

额外优化建议

  • 给工作线程增加异常捕获逻辑,网络异常时弹出提示告知用户
  • 点击计算按钮后可以先将按钮置灰,避免用户重复点击发送重复请求,拿到结果后再恢复按钮可用状态
  • 增加输入校验逻辑,避免用户输入非数字内容触发parseInt崩溃

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 20:54:04