5

我有以下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最终一样工作,我无法将对话框的返回值分配给它。

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

4

6 回答 6

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];
}
于 2011-01-20T17:04:30.297 回答
3

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

对 JOptionPane 的 showXXXDialog() 之一的调用是阻塞的,直到用户选择 ok/cancel/etc。一般来说,您通常不会在事件调度线程 (EDT) 上放置如此缓慢的阻塞指令,因为每个其他 GUI 组件都会冻结。所以,不把它放在 EDT 上的直觉是好的,但它也是错误的。原因正如其他人所说,该方法创建 GUI 组件,这应该始终在 EDT 上完成。但是阻塞呢?您会注意到,即使您确实在 EDT 上运行它,它也能正常工作。原因在源代码中找到。JOptionPane 类创建一个 Dialog 对象,然后调用 show() 和 dispose(),第一个是阻塞线程的。如果您阅读评论(或 javadoc),您会看到它说明了该方法:

如果对话框是模态的并且尚不可见,则此调用将不会返回,直到通过调用 hide 或 dispose 隐藏对话框。允许从事件调度线程显示模式对话框,因为工具包将确保另一个事件泵运行,而调用此方法的事件泵被阻塞。

因此,尽管 JOptionPane 被阻塞,但在 EDT 上运行它是完全安全的。显然,在 EDT 之外调用 Dialog 的 show() 方法是安全的,但对于 JOptionPane 则不是这样,因为它的方法是创建 GUI 组件、添加侦听器、在模态时访问其他容器并阻止对它们的输入等。你不需要希望所有这些都在 EDT 之外完成,因为它不是线程安全的并且可能存在问题。诚然,在 EDT 之外使用 JOptionPane 时,我从未遇到过问题,因此可能性似乎很低,但它们肯定是有可能的。为对话框的容器传入一个 null 并且只将不可变对象(如字符串)作为参数提供给字段将显着减少(据我所知甚至可能消除)发生坏事的机会,因为所有相关的 GUI 组件都是制作的并在它们不可见时在同一个线程中访问。但是,您应该安全并将其放在 EDT 上。调用 SwingUtilities.invokeAndWait() 并不难。

于 2012-07-21T04:55:44.527 回答
2
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);
    }
  }
于 2011-01-20T17:06:17.537 回答
2

与流行的看法相反,您不需要分派到 AWT(EventQueue)线程来显示对话框。所以就展示一下吧。

当您执行 JOptionPane,showMessge() 时,您的线程 (Thread.currentThread()) 将执行 wait(),并且会弹出对话框。使用 showMessage 之后的结果就可以了。

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

于 2011-01-20T20:51:59.680 回答
1

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

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

PS你如何定义一个非事件调度线程?

于 2011-01-20T17:39:30.480 回答
0

从 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();
}

感谢 jtahlborn 如何将结果从 EDT 传递回不同的线程?

于 2017-10-05T15:12:51.707 回答