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

SwingWorker实现C/S应用:线程数达10后无法处理请求的解决方法

问题解决:SwingWorker线程池耗尽与GUI切换连接优化

问题分析

  1. SwingWorker线程池限制:SwingWorker默认线程池大小为10,而你的服务器中每个客户端连接都会占用一个SwingWorker线程(因为processClient里的线程是无限循环读取数据,会一直占用线程资源),当连接数达到10后,线程池被完全耗尽,新的客户端请求无法得到处理。
  2. 客户端重复连接:每次切换GUI(比如从客户界面切到管理员界面)时,旧的Socket连接没有关闭,新界面又会创建新的连接,导致服务器端不断生成新的处理线程,进一步加速线程池耗尽。

解决方案

1. 替换SwingWorker为全局自定义线程池

放弃使用SwingWorker的默认线程池,改用ExecutorService创建全局线程池,统一管理线程资源,避免线程数限制。注意不要在每次处理客户端时都新建线程池,否则会造成资源浪费。

修改后的服务器代码:

public class Testserver {
    private ServerSocket serverSocket;
    private final int PORT = 5432;
    // 全局线程池,根据CPU核心数合理设置大小
    private final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);

    public Testserver() {
        System.out.println(Thread.currentThread().getName());
        try {
            serverSocket = new ServerSocket(PORT);
            System.out.println("等待客户端连接");
        } catch (IOException ex) {
            Logger.getLogger(Testserver.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void listenForClients() {
        try {
            while (true) {
                Socket client = serverSocket.accept();
                System.out.println("客户端已连接");
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(client.getOutputStream());
                ObjectInputStream objectInputStream = new ObjectInputStream(client.getInputStream());
                // 将客户端连接和流传入处理方法
                processClient(client, objectInputStream, objectOutputStream);
            }
        } catch (IOException ex) {
            Logger.getLogger(Testserver.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new Testserver().listenForClients();
        });
    }

    private void processClient(Socket client, ObjectInputStream objectInputStream, ObjectOutputStream objectOutputStream) {
        executor.execute(() -> {
            System.out.println(Thread.currentThread().getName());
            try {
                String messageFromClient;
                // 用while循环替代do-while,避免空指针问题
                while ((messageFromClient = (String) objectInputStream.readObject()) != null) {
                    System.out.println("[客户端] " + messageFromClient);
                    if (messageFromClient.equalsIgnoreCase("Exit")) {
                        System.out.println("已关闭与客户端的连接: " + client);
                        closeConnections(client, objectInputStream, objectOutputStream);
                        break;
                    }
                }
            } catch (IOException ex) {
                ex.printStackTrace();
                closeConnections(client, objectInputStream, objectOutputStream);
            } catch (ClassNotFoundException ex) {
                System.out.println("处理客户端时发生类未找到异常: " + ex.getMessage());
                closeConnections(client, objectInputStream, objectOutputStream);
            } catch (Exception ex) {
                ex.printStackTrace();
                System.out.println("处理客户端时发生异常: " + ex.getMessage());
                closeConnections(client, objectInputStream, objectOutputStream);
            }
        });
    }

    // 统一关闭连接和流的工具方法
    private void closeConnections(Socket client, ObjectInputStream ois, ObjectOutputStream oos) {
        try {
            if (ois != null) ois.close();
            if (oos != null) oos.close();
            if (client != null) client.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

2. 修复客户端切换GUI时的重复连接问题

每次切换GUI前,关闭当前的Socket连接和流,避免服务器端残留大量无效连接:

修改CustomerGUI的actionPerformed方法,新增连接关闭逻辑:

@Override
public void actionPerformed(ActionEvent e) {
    if (e.getSource() == btnAdminGUI) {
        // 切换前关闭当前连接
        closeConnections();
        new AdminGUI().setGUI();
        dispose();
    } else if (e.getSource() == btnAddCustomer) {
        try {
            objectOutputStream.writeObject("添加了一个客户");
            objectOutputStream.flush();
        } catch (IOException ex) {
            Logger.getLogger(CustomerGUI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

// 关闭当前客户端连接的方法
private void closeConnections() {
    try {
        if (objectInputStream != null) objectInputStream.close();
        if (objectOutputStream != null) objectOutputStream.close();
        if (server != null) server.close();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

同样修改AdminGUI的actionPerformed方法:

@Override
public void actionPerformed(ActionEvent e) {
    if (e.getSource() == btnCustomerGUI) {
        // 切换前关闭当前连接
        closeConnections();
        new CustomerGUI().setGUI();
        dispose();
    } else if (e.getSource() == btnAddAdmin) {
        try {
            objectOutputStream.writeObject("添加了一个管理员");
            objectOutputStream.flush();
        } catch (IOException ex) {
            Logger.getLogger(AdminGUI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

// 关闭当前客户端连接的方法
private void closeConnections() {
    try {
        if (objectInputStream != null) objectInputStream.close();
        if (objectOutputStream != null) objectOutputStream.close();
        if (server != null) server.close();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

关键改进点

  • 全局线程池:统一管理线程资源,避免频繁创建销毁线程池,可根据服务器性能调整线程池大小(比如CPU核心数的1-2倍)。
  • 资源及时清理:无论客户端正常退出还是异常断开,都主动关闭Socket和流,释放服务器端资源。
  • 避免无效连接:客户端切换GUI时关闭旧连接,减少服务器端的无效连接和线程占用,从根源降低线程池耗尽的风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 07:40:21