有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何在非事件调度线程中间提示确认对话框

我有以下fun将由非事件调度线程执行。在线程的中间,我想要一个

  1. 一个确认框弹出。线程暂停其执行
  2. 用户做出选择
  3. 线程将获得选择并继续执行

然而,我发现用线程安全的方式来实现这一点并不容易,因为对话框应该由事件调度线程来显示。我试着

public int fun()
{
    // The following code will be executed by non event dispatching thread.
    final int choice;
    SwingUtilities.invokeAndWait(new Runnable() {

        @Override
        public void run() {
            // Error.
            choice = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
        }            
    });
    return choice;
}

当然,这不会起作用,因为choice是最终的,我无法将对话框返回的值分配给它

实现上述三个目标的正确方法是什么


共 (6) 个答案

  1. # 1 楼答案

    我有点厌倦了人们说事情是这样或那样的,但却不知道他们在说什么。我来这里想知道应该在哪个线程JOptionPane上运行,但我得到了相互矛盾的答案,没有真正的证据支持这两个观点。嗯,我自己研究过,我对这个答案很满意,所以我将与大家分享

    在用户选择ok/cancel/etc之前,对JOptionPane的showXXXDialog()之一的调用一直处于阻塞状态。通常情况下,您不会在事件调度线程(EDT)上放置这种缓慢的阻塞指令,因为其他所有GUI组件都将冻结。所以,本能地不把它放在EDT上是好的,但这也是错误的。原因正如其他一些人所说,该方法创建GUI组件,这应该始终在EDT上完成。但是封锁呢?你会注意到,即使你在EDT上运行它,它也可以正常工作。原因可以在源代码中找到。JOptionPane类创建一个对话框对象,然后调用show()和dispose(),第一个是阻止线程的。如果您阅读了注释(或javadoc),您将看到它对该方法的描述:

    If the dialog is modal and is not already visible, this call will not return until the dialog is hidden by calling hide or dispose. It is permissible to show modal dialogs from the event dispatching thread because the toolkit will ensure that another event pump runs while the one which invoked this method is blocked.

    因此,在EDT上运行JOptionPane是完全安全的,尽管它被阻塞了。显然,在EDT之外调用Dialog的show()方法是安全的,但在JOptionPane中情况并非如此,因为它的方法是创建GUI组件、添加侦听器、在模态时访问其他容器并阻止对它们的输入等。您不希望所有这些都在EDT之外完成,因为它不是线程安全的,而且可能存在问题。诚然,我从未见过在EDT之外使用JOptionPane时出现问题,因此可能性似乎很低,但这是最有可能的。为对话框的容器传递一个null,并且只给字段提供不可变的对象(比如字符串)作为参数,这将显著降低(甚至可能消除)发生错误的可能性,因为所有相关的GUI组件都是在同一个线程中创建和访问的,而它们是不可见的。但是,你应该注意安全,把它放在EDT上。称之为“勇气”并不难。invokeAndWait()

  2. # 2 楼答案

    从EDT启动JOptionPane,并通过FutureTask返回值

    public int fun()
    {
        FutureTask<Integer> dialogTask = new FutureTask<Integer>(new Callable<Integer>() 
        {
            @Override public Integer call() 
            {
                return JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
            }
        });
        SwingUtilities.invokeLater(dialogTask);
        int choice  = dialogTask.get();
    }
    

    归功于杰塔伯恩 How to pass results from EDT back to a different thread?

  3. # 3 楼答案

    与流行的观点相反,你不需要分派到AWT(EventQueue)线程来显示对话框。那就展示出来吧

    在执行JOptionPane时,showMessge()您的线程(thread.currentThread())将执行wait(),对话框将弹出。在showMessage之后使用结果,你就可以开始了

    因此:
    choice = JOptionPane.showConfirmDialog(this, message, title, JOptionPane.YES_NO_OPTION);

  4. # 4 楼答案

    public int fun() throws InterruptedException, InvocationTargetException {
        // The following code will be executed by non event dispatching thread.
        ChoiceRunnable runabble = new ChoiceRunnable();
        SwingUtilities.invokeAndWait(runabble);
    
        return runabble.choice;
      }
    
      class ChoiceRunnable implements Runnable {
        private int choice;
    
        public void run() {
          choice = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
        }
      }
    
  5. # 5 楼答案

    你试过:

    public int fun()
    {
        // The following code will be executed by non event dispatching thread.
        final int[] choice = new int[1];
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                // Error.
                choice[0] = JOptionPane.showConfirmDialog(SaveToCloudJDialog.this, message, title, JOptionPane.YES_NO_OPTION);
            }            
        });
        return choice[0];
    }
    
  6. # 6 楼答案

    也许我不明白这个问题,但我也没有得到答案。。。如果希望调用线程阻止对fun()的调用,为什么要在一个新(并行)线程中显示JOptionPane?这还不够吗

    public int fun() {
        return JOptionPane.showConfirmDialog(null, message, title, JOptionPane.YES_NO_OPTION);
    } 
    

    PS如何定义非事件调度线程