您似乎知道,Swing 是一个单线程框架,这意味着在事件调度线程的上下文中运行的任何东西都会阻止它更新屏幕或响应用户输入。
基本的解决方案是使用Thread 来运行批处理,但这会引发与 UI 更新同步的问题,因为您不应该从 EDT 上下文之外修改 UI 或与 UI 交互.
更好的解决方案是使用SwingWorker,它使您能够在后台运行长时间运行的任务,但使您能够在上下文中进行publish 更新和process 更新在 EDT 中,它还为您提供了一个 done 方法,该方法在 doInBackground 方法退出后调用,并在 EDT 的上下文中调用。
最后,它为您提供了一个cancel 选项——然而,这正是问题所在。大概您将在辅助线程中从进程中读取输入,并且将是 waiting 以使进程在您启动它的同一线程 (SwingWorker) 中退出。 SwingWorker 依赖于 Thread 的 interrupt 功能,这可能不会触发 waitFor 方法返回。
现在阅读了Process 文档,waitFor 确实抛出了InterruptedException
如果当前线程被另一个线程中断,而它正在
等待,然后等待结束并抛出 InterruptedException。
这表明当done 被调用时,你需要调用isCancelled 来检查worker 是否被取消。如果是,您需要在Process 上调用destroy 并关闭您可能正在运行的任何辅助Threads。
您可以使用额外的SwingWorker 来读取进程的输入,并利用它的publish/process 功能来更新日志。
这意味着,您将启动一个SwingWorker 来执行您的外部进程。这可能是为了响应某些事件,例如按钮按下。
当这个worker的doInBackground方法被调用时,它会执行外部进程并调用Process#waitFor。这将阻止doInBackground 方法返回,直到进程退出。
在调用Process#waitFor 之前,您可以创建另一个SwingWorker 并将Process 的OutputStream 传递给它。这将允许该工作人员独立处理流程的输出。然后,您可以使用它通过SwingWorker 的publish/process 功能将进程的输出发送回EDT,该功能可以添加到JTextArea 之类的东西中。
这将为您节省很多处理SwingUtilities.invokeLater 的麻烦。
你需要第二份工作吗?这取决于你想让工人做什么。我倾向于在单独的线程中处理外部进程的所有输出,并允许创建该进程的人使用waitFor,它更多地隔离了责任,并防止 IO 被锁定到永远不会到达waitFor,但这就是只有我。
查看Concurrency in Swing了解更多详情