SwingWorker实现C/S应用:线程数达10后无法处理请求的解决方法
问题解决:SwingWorker线程池耗尽与GUI切换连接优化
问题分析
- SwingWorker线程池限制:SwingWorker默认线程池大小为10,而你的服务器中每个客户端连接都会占用一个SwingWorker线程(因为
processClient里的线程是无限循环读取数据,会一直占用线程资源),当连接数达到10后,线程池被完全耗尽,新的客户端请求无法得到处理。 - 客户端重复连接:每次切换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
相关产品推荐
相关产品推荐

