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

SwingUtilities.InvokeAndWait()未执行Runnable代码的解决求助

问题排查与修复方案

我来帮你解决这个InvokeAndWait()无响应的问题——你的核心需求是在Socket连接接入时,让用户选择目录,并且要精准控制获取选中目录的时机对吧?先说说为什么你的代码会失效,再给你修复后的版本。

核心问题分析

  1. Swing组件的线程安全违规:如果你的JFileChooser是在非EDT(Swing事件调度线程)里创建的,后续在EDT中调用它的showOpenDialog()会出现各种异常行为,包括完全无响应——Swing要求所有组件的创建和UI操作必须在EDT中完成,这是硬性规则。
  2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:39:01