如何检索必须在另一个线程上计算的值
在许多情况下,线程A需要一个必须在线程B上计算的值。(最常见的是,B == EDT。)考虑这个例子:
String host; SwingUtilities.invokeAndWait(new Runnable() { public void run() { host = JOptionPane.showInputDialog("Enter host name: "); } }); openConnection(host);
当然,这不会编译,因为匿名内部类不允许写入host
。 让这个最简单,最干净的方法是什么? 我已经在下面列出了我所知道的方式。
没有:
使用Future
和可能的Callable
以及ExecutorService
。 Future
基本上是您想要的精确程序化表达: 未来答案的承诺,以及在答案可用之前阻止的能力 。 Future还会自动包装并为您呈现潜在的并发噩梦和复杂性的完整复杂的一些例子。 这是一件好事,因为它迫使你处理它们,当一个自己动手的解决方案可能永远不会揭示它们,除了一些exception的,难以诊断的行为。
public void askForAnAnswer() throws TimeoutException, InterruptedException, ExecutionException { Future theAnswerF = getMeAnAnswer(); String theAnswer = theAnswerF.get(); } public Future getMeAnAnswer() { Future 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
的SwingWorker 。 请不要重复,请查看此SO问题 。
final SynchronousQueue host = new SynchronousQueue (); SwingUtilities.invokeAndWait(new Runnable() { public void run() { host.add(JOptionPane.showInputDialog("Enter host name: ")); } }); openConnection(host.poll(1, TimeUnit.SECONDS));
详细:使用内部类
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);
我建议创建一个类来处理这个,下面的示例:
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() ;
我将通过使类抽象并使用generics来允许返回任何类型的值来扩展此方法。
abstract class SyncUserDataGeneric implements Runnable { private Type value ; public void run() { value = process(); } public Type getValue() { return value ; } public abstract Type process() ; } String host; SyncUserDataGeneric doHostnameGen ; doHostnameGen = new SyncUserDataGeneric () { public String process() { return JOptionPane.showInputDialog("Enter host name: "); } }; host = doHostnameGen.getValue() ;
编辑:检查是否从EventDispatchThread运行。
if (SwingUtilities.isEventDispatchThread()) { host = doHostnameGen.process() ; } else { SwingUtilities.invokeAndWait(doHostnameGen) ; host = doHostnameGen.getValue() ; }
请注意:作者不喜欢这个答案,这只是对具体问题的“嗤之以鼻”的答案。
如果你只是在等待最低限度地修改上面的代码以便它可以工作,那么你就是在处理内部类只能参考到决赛的问题。 创建一个命名的内部类而不是匿名的内部类,并在该类中创建一个String主机字段。 将该实例传递给invokeAndWait()
。 但是,在我的拙见中,这仍然很糟糕,远远不如我上面引用的Future<>
方法。
class FooWidget implements Runnable() { AtomicReference host = new AtomicReference (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());
优雅? 使用primefaces变量
final AtomicReference host = new AtomicReference (); SwingUtilities.invokeAndWait(new Runnable() { public void run() { host.set(JOptionPane.showInputDialog("Enter host name: ")); } }); openConnection(host.get());
哈克:使用数组
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?