从 EDT 线程在非 EDT 线程上调用 Future.get() 是否可以?

Is it OK to call Future.get() on a non-EDT thread from the EDT thread?

这是在单元测试的上下文中(碰巧)。

在测试结束时,无论结果如何,我都希望代码检查 JFileChooser 对话框是否存在(可见性)...如果可见,则将其关闭。

当然,有多种关闭对话的方式,但为了模仿人类行为(并举例说明我在这里关心的这类问题),我选择使用 java.awt.Robot。后者的方法在非 EDT 线程中应该是 运行。

事实上,我已经扩展 Robot 以包含一个名为 type_input 的便捷方法,所以

robot.type_input( KeyEvent.VK_F4, KeyEvent.VK_ALT )

先按 Alt,然后按 F4,然后松开 F4,再按 Alt:就像人类解雇 window/dialog。

我使用 invokeAndWait 提交了 Runnable,因为我不希望代码 运行 进入下一个测试,直到关闭此对话框。我必须测试 EDT 中的可见性和焦点。但正如我所说,Robot 方法在非 EDT 中必须是 运行。

在 EDT 中这样 get() 有什么潜在的问题吗?它会不会导致 GUI 无响应?问题是,我听说该框架在某些条件下能够 "starting a new EDT pump"。我不得不承认这是我对 EDT 相关问题了解最少的方面之一...

import java.awt.EventQueue;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;

class MainFrame extends JFrame {
    JFileChooser update_target_file_chooser;
    JDialog file_chooser_dlg;

    // ... rest of class

}

public class ThreadWithinThread {

    public static void main(String[] args) throws InvocationTargetException, InterruptedException {

        final ExecutorService thread_pool_exec_serv = Executors.newFixedThreadPool( 5 );

        class DismissDlg implements Runnable {
            MainFrame main_frame;
            Robot robot;

            @Override
            public void run() {
                boolean focus_on_dlg = main_frame.file_chooser_dlg.hasFocus();
                if( main_frame.file_chooser_dlg.isVisible() ){
                    if( ! focus_on_dlg ){
                        main_frame.file_chooser_dlg.requestFocus();
                    }
                    class AltF4 implements Callable<Void>{
                        public Void call(){
                            return robot.type_input( KeyEvent.VK_F4, KeyEvent.VK_ALT );
                        }
                    }
                    Future<Void> future_result = thread_pool_exec_serv.submit( new AltF4() );
                    try {
                        // this is the line I'm worried about
                        future_result.get();
                    } catch (InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        EventQueue.invokeAndWait( new DismissDlg() );
    }

}

以后

正如我在回复中所说,这不是针对手头案例的特定实际解决方案:我真的想了解另一个 EDT "event pump" 是否以及何时以 [=19 启动=] 阻止美国东部时间。 正如我所说,我发现这整个方面很难理解。我对 JOptionPane 的静态方法(在 EDT 中必须是 运行,请参阅 SO ad nauseam 上的帖子)向我证明这些方法(确实会阻止 EDT!)实际上并没有似乎阻止了 GUI 的运行(注意不要混淆这些 JOptionPanes,或者更确切地说,它们的 JDialogs,是模态的还是非模态的)。

我的理解是,这是因为 "the framework" 然后启动了另一个 "event pump"。那么它可以在这里这样做吗?

所以,从 JavaDocs

Waits if necessary for the computation to complete, and then retrieves its result.

这表明该方法是一个阻塞方法,如果在事件调度线程的上下文中有一件事情是你不应该做的,那就是在事件调度线程的上下文中调用一个阻塞方法

啊,但是怎么办?您可以改用 SwingWorker,它在内部使用它自己的 ExecutorService,或者 submit 工人使用您自己的 ExecutorServiceSwingWorker 实现 Runnable ),然后您应该能够使用 PropertyChangeListener 来监视 SwingWorker 的状态,当它是 DONE 时,您可以从它的 get 方法中检索值而不会阻塞

在 EDT 上执行必要的步骤,并向测试线程发出是否需要额外步骤的信号:

class PrepDlgDismiss implements Runnable {
  boolean file_chooser_visible;
  @Override
  public void run() {
    boolean focus_on_dlg = main_frame.file_chooser_dlg.hasFocus();
    if( main_frame.file_chooser_dlg.isVisible() ){
      file_chooser_visible = true;
      if( ! focus_on_dlg ){
        main_frame.file_chooser_dlg.requestFocus();
      }
    }
  }
}
PrepDlgDismiss task = new PrepDlgDismiss();
EventQueue.invokeAndWait( task );
if( task.file_chooser_visible ){
  robot.type_input( KeyEvent.VK_F4, KeyEvent.VK_ALT );
}