3

在很多情况下,线程 A 需要一个必须在线程 B 上计算的值。(最常见的是,B == EDT。)考虑这个例子:

String host;
SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        host = JOptionPane.showInputDialog("Enter host name: ");
    }
});
openConnection(host);

当然,这不会编译,因为匿名内部类不允许写入host. 让它工作的最简单、最干净的方法是什么?我在下面列出了我所知道的方式。

4

7 回答 7

2

不:

使用一个Future<T>和可能Callable<T>和一个ExecutorService。AFuture基本上是您想要的精确编程表达:对未来答案的承诺,以及在答案可用之前阻止的能力。Future 还会自动为您包装并呈现一个完整的、复杂的潜在并发噩梦和复杂性,并将其呈现为一些明确定义的异常。这是一件好事,因为它迫使你处理它们,而你自己的解决方案可能永远不会揭示它们,除非是一些异常的、难以诊断的行为。

public void askForAnAnswer() throws TimeoutException, InterruptedException, ExecutionException
{
  Future<String> theAnswerF = getMeAnAnswer();
  String theAnswer = theAnswerF.get();

}

public Future<String> getMeAnAnswer()
{
  Future<String> promise = null;
  // spin off thread/executor, whatever.
  SwingUtilities.invokeAndWait(new Runnable() {
  public void run() {
    host = JOptionPane.showInputDialog("Enter host name: ");
  }
 });
 // ... to wrap a Future around this.

  return promise;
}

对于您的特定情况,您可能可以在实现Future. 而不是重复,请看一下这个SO question

于 2010-10-20T20:45:49.520 回答
1
    final SynchronousQueue<String> host = new SynchronousQueue<String>();

    SwingUtilities.invokeAndWait(new Runnable() {

        public void run() {
            host.add(JOptionPane.showInputDialog("Enter host name: "));
        }

    });

    openConnection(host.poll(1, TimeUnit.SECONDS));
于 2010-10-20T21:12:03.317 回答
0

I'll recommend to create a class to handle this, samples below:

class SyncUserData implements Runnable {
    private String value ;

    public void run() {
        value = JOptionPane.showInputDialog("Enter host name: ") ;
    }

    public String getValue() {
        return value ;
    }
}
// Using an instance of the class launch the popup and get the data.
String host;
SyncUserData syncData = new SyncUserData() ;
SwingUtilities.invokeAndWait(syncData);
host = syncData.getValue() ;

I'll extend this approach by making the class abstract and using generics to allow any type of values to be return.

abstract class SyncUserDataGeneric<Type> implements Runnable {
    private Type value ;
    public void run() {
        value = process();
    }
    public Type getValue() {
        return value ;
    }

    public abstract Type process() ;
}

String host;
SyncUserDataGeneric<String> doHostnameGen ;

doHostnameGen = new SyncUserDataGeneric<String>() {
    public String process() {
        return JOptionPane.showInputDialog("Enter host name: ");
    }
};

host = doHostnameGen.getValue() ;

EDIT: Checks if running from the EventDispatchThread.

if (SwingUtilities.isEventDispatchThread()) {
    host = doHostnameGen.process() ;
} else {
    SwingUtilities.invokeAndWait(doHostnameGen) ;
    host = doHostnameGen.getValue() ;
}
于 2010-10-20T22:53:36.880 回答
0

请注意:作者不喜欢这个答案,这只是对特定问题的“鼻子上”的回答。

如果您只是在等待对上述代码进行最小程度的修改以使其正常工作,那么您正在处理 inner-class-can-only-refer-to-finals 问题。划分出一个命名的内部类而不是一个匿名的内部类,并在该类中创建一个 String 主机字段。将该实例传递给invokeAndWait(). 但这仍然很恶心,在我看来,远不如Future<>我上面引用的方法。

class FooWidget implements Runnable() {
  AtomicReference<String> host = new AtomicReference<String>(null);
  @Override
  public void run() {
    host.set(JOptionPane.showInputDialog("Enter host name: "));
  }
}

...

FooWidget foo = new FooWidget();
SwingUtilities.invokeAndWait(foo);
if (foo.host.get() == null) { throw new SomethingWentWrongException(); }
openConnection(foo.host.get());
于 2010-10-20T23:00:03.760 回答
0

详细:使用内部类

class HostGetter implements Runnable{
    volatile String host;
    public void run() {
        host = JOptionPane.showInputDialog("Enter host name: ");
    }
}
HostGetter hg = new HostGetter();
SwingUtilities.invokeAndWait(hg);
openConnection(hg.host);
于 2010-10-20T20:18:56.637 回答
-1

优雅的?使用原子变量

final AtomicReference<String> host = new AtomicReference<String>();
SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        host.set(JOptionPane.showInputDialog("Enter host name: "));
    }
});
openConnection(host.get());
于 2010-10-20T20:18:20.460 回答
-1

技巧:使用数组

final String[] host = new String[1];
SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        host[0] = JOptionPane.showInputDialog("Enter host name: ");
    }
});
openConnection(host[0]); //maybe not guaranteed to be visible by the memory model?
于 2010-10-20T20:19:38.053 回答