简体   繁体   English

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

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

This is in the context of unit testing (as it happens). 这是在单元测试的上下文中发生的。

At the end of a test, whatever the result, I want the code to check for the presence (visibility) of a JFileChooser dialog... and if visible, dismiss it. 在测试结束时,无论结果如何,我都希望代码检查JFileChooser对话框的存在(可见性)...,如果可见,则将其关闭。

There are different ways of dismissing a dialog, of course, but to mimic human action (and to give an example of the sort of question I'm concerned with here) I choose to use java.awt.Robot . 当然,有多种消除对话框的方法,但是为了模仿人类的行为(并在此处举例说明我所关注的问题),我选择使用java.awt.Robot The latter's methods should be run in a non-EDT thread. 后者的方法应在非EDT线程中运行。

In fact I have extended Robot to include a convenience method called type_input , so 实际上,我已经对Robot进行了扩展,以包括一个名为type_input的便捷方法,因此

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

presses first Alt, then F4, then releases F4 and then Alt: like a human dismissing a window/dialog. 先按Alt,然后按F4,然后再释放F4,再按Alt:就像人在关闭窗口/对话框一样。

I submit the Runnable using invokeAndWait because I don't want the code to run on to the next test until this dialog has been dismissed. 我使用invokeAndWait提交Runnable因为在此对话框关闭之前,我不希望代码继续进行下一个测试。 I have to test for visibility and focus in the EDT. 我必须测试EDT中的可见性和焦点。 But the Robot method must, as I said, be run in a non-EDT. 但是,正如我所说,Robot方法必须在非EDT中运行。

Are there any potential problems with going get() like this in the EDT? 在EDT中像这样进行get()有任何潜在的问题吗? Would it potentially lead to unresponsiveness of the GUI? 可能会导致GUI的无响应吗? The thing is, I have heard that the framework is capable of "starting a new EDT pump" under certain conditions. 事实是,我听说该框架能够在某些条件下“启动新的EDT泵”。 I have to confess this is one of the aspects of EDT-related matters that I feel I know least about... 我不得不承认这是我最不了解的与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() );
    }

}

later 后来

As I have said in my responses, this is not about a particular practical solution to the case in hand: I'm really trying to understand whether and when another EDT "event pump" starts up with Future.get() blocking the EDT. 正如我在答复中所说的,这并不是针对当前情况的特定实用解决方案:我实际上是在试图了解,是否以及何时使用Future.get()启动另一个EDT“事件泵”以阻止EDT。 As I say I find this whole aspect difficult to understand. 正如我所说,我觉得这整个方面很难理解。 My use of JOptionPane 's static methods (which must be run in the EDT, see posts on SO ad nauseam) demonstrates to me that these methods (which do block the EDT!) don't actually seem to prevent the GUI from functioning (NB not to be confused with whether these JOptionPane s, or rather their JDialog s, are modal or modeless). 我对JOptionPane的静态方法(必须在EDT中运行,请参阅SO ad nauseam上的帖子)的使用向我展示了这些方法(确实会阻止EDT!)实际上似乎并未阻止GUI起作用(注意不要将这些JOptionPane或它们的JDialog是模态或无模的混淆。

My understanding is that this is due to the fact that "the framework" then starts another "event pump". 我的理解是,这是因为“框架”随后启动了另一个“事件泵”。 So could it do so here? 可以在这里这样做吗?

So, from the JavaDocs 因此,从JavaDocs

Waits if necessary for the computation to complete, and then retrieves its result. 必要时等待计算完成,然后检索其结果。

Which suggests that the method is a blocking method, if there's one thing you shouldn't do from within the context of the Event Dispatching Thread, it's call a blocking method within the context of the Event Dispatching Thread 这表明该方法是一种阻塞方法,如果您不应该在事件调度线程的上下文中做某件事,那么它被称为事件调度线程的上下文中的阻塞方法

Ah, but what to do? 啊,但是该怎么办? You could use a SwingWorker instead, which internally uses it's own ExecutorService , or submit the worker to your own ExecutorService ( SwingWorker implements Runnable ), you should then be able to use a PropertyChangeListener to monitor the state of the SwingWorker and when it's DONE , you can retrieve the value from it's get method without blocking 您可以改为使用SwingWorker ,该SwingWorker在内部使用它自己的ExecutorService ,或者submit工作程序submit到您自己的ExecutorServiceSwingWorker实现Runnable ),然后您应该能够使用PropertyChangeListener来监视SwingWorker的状态以及何时将其DONE 。可以从它的get方法中检索值而不会阻塞

Perform necessary steps on the EDT, and signal the test thread whether an additional step is needed: 在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 );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM