SwingUtilities.InvokeAndWait()未执行Runnable代码的解决求助
问题排查与修复方案
我来帮你解决这个InvokeAndWait()无响应的问题——你的核心需求是在Socket连接接入时,让用户选择目录,并且要精准控制获取选中目录的时机对吧?先说说为什么你的代码会失效,再给你修复后的版本。
核心问题分析
- Swing组件的线程安全违规:如果你的
JFileChooser是在非EDT(Swing事件调度线程)里创建的,后续在EDT中调用它的showOpenDialog()会出现各种异常行为,包括完全无响应——Swing要求所有组件的创建和UI操作必须在EDT中完成,这是硬性规则。 - InvokeAndWait的死锁风险:
InvokeAndWait()会阻塞当前线程(你的ServerSocket监听线程),直到EDT执行完指定任务。但showOpenDialog()本身也会阻塞EDT直到用户操作完成,这种嵌套阻塞很容易导致线程调度卡死,看起来就是代码完全没执行。
修复后的完整代码
import javax.swing.*; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; public class FileTransfListener implements Runnable { private final AtomicReference<String> selectedDir = new AtomicReference<>(); private JFileChooser dirChooser; // 确保JFileChooser在EDT中初始化,遵守Swing线程安全规则 public FileTransfListener() { SwingUtilities.invokeLater(() -> { dirChooser = new JFileChooser(); dirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); // 强制只选目录,符合你的需求 }); } @Override public void run() { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(60905); System.out.println("监听端口60905,等待连接..."); } catch (IOException e1) { System.err.println("启动ServerSocket失败: " + e1.getMessage()); return; } while (true) { try (Socket socket = serverSocket.accept()) { System.out.println("收到来自" + socket.getInetAddress() + "的连接"); selectedDir.set(null); // 每次连接重置选中状态 // 用CompletableFuture实现"等待EDT完成对话框操作"的逻辑,替代InvokeAndWait CompletableFuture<Void> dialogFuture = new CompletableFuture<>(); SwingUtilities.invokeLater(() -> { int result = dirChooser.showOpenDialog(null); if (result == JFileChooser.APPROVE_OPTION) { selectedDir.set(dirChooser.getSelectedFile().getAbsolutePath()); } dialogFuture.complete(null); // 通知监听线程:对话框操作完成 }); // 阻塞当前线程,直到用户选完目录或取消 dialogFuture.join(); String dir = selectedDir.get(); System.out.println("用户选择的目录: " + (dir == null ? "用户取消选择" : dir)); // 这里可以继续处理Socket的输入输出逻辑... } catch (IOException ex) { ex.printStackTrace(); try { if (serverSocket != null) serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } break; } } } }
关键修复点说明
- EDT中创建组件:构造方法里用
InvokeLater()初始化JFileChooser,严格遵守Swing的线程安全要求,从根源避免组件状态异常。 - 用CompletableFuture替代InvokeAndWait:既保证对话框在EDT中执行,又能让监听线程安全等待用户操作完成,完全规避了
InvokeAndWait()的死锁风险。 - 强制目录选择:添加
setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY),确保用户只能选目录,符合你的业务需求。 - 自动资源管理:用try-with-resources语法自动关闭
Socket,避免资源泄漏。
为什么你的原代码没反应?
如果你的dirChooser是在非EDT线程创建的,它的内部UI状态会处于不一致的状态,当InvokeAndWait()让EDT去调用showOpenDialog()时,组件根本无法正常渲染或响应事件,看起来就像代码完全没执行。另外,如果EDT当时被其他任务阻塞,InvokeAndWait()的任务也会被无限延迟。
内容的提问来源于stack exchange,提问作者Giuseppe Ayanami De Frenza
相关产品推荐
相关产品推荐

