如何检索必须在另一个线程上计算的值

发布于 2024-09-28 12:20:06 字数 351 浏览 10 评论 0原文

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

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

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

There are many cases where thread A requires a value that must be computed on thread B. (Most commonly, B == EDT.) Consider this example:

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

Of course, this doesn't compile, because the anonymous inner class isn't allowed to write to host. What's the simplest, cleanest way to get this to work? I've included the ways I know of below.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

森林散布 2024-10-05 12:20:06

否:

使用 Future;可能还有 CallableExecutorService未来基本上是您想要的内容的精确编程表达:对未来答案的承诺,以及在答案可用之前阻止的能力。 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;
}

对于您的具体情况,您可以在 SwingWorker 上构建 实现了 Future。不要重复,请看一下这个SO问题

No:

Use a Future<T> and possibly Callable<T> and an ExecutorService. A Future is basically an exact programmatic expression of what you want: a promise of a future answer, and the ability to block until the answer is available. Future also automatically wraps and presents for you an entire, complicated farrago of potential concurrency nightmares and complexities into a few clearly defined exceptions. This is a good thing because it forces you to deal with them, when a roll-your-own solution would likely never reveal them except in some aberrant, difficult to diagnose behavior.

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;
}

For your specific case, you can probably build on a SwingWorker which implements Future. Rather than duplicate, please take a look at this SO question.

季末如歌 2024-10-05 12:20:06
    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));
    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));
成熟稳重的好男人 2024-10-05 12:20:06

详细:使用内部类

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);

Verbose: Use an inner class

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);
小草泠泠 2024-10-05 12:20:06

我建议创建一个类来处理此问题,示例如下:

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

我将通过使类抽象并使用泛型来允许返回任何类型的值来扩展此方法。

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

编辑:检查是否从 EventDispatchThread 运行。

if (SwingUtilities.isEventDispatchThread()) {
    host = doHostnameGen.process() ;
} else {
    SwingUtilities.invokeAndWait(doHostnameGen) ;
    host = doHostnameGen.getValue() ;
}

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() ;
}
毁虫ゝ 2024-10-05 12:20:06

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

如果您只是想等待最小程度地修改上面的代码,那么它有效,您正在处理内部类只能引用决赛的问题。创建一个命名内部类而不是匿名内部类,并在该类中创建一个 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());

Please note: the author doesn't like this answer, it's just the "on the nose" answer to the specific question.

If you're only looking for a wait to minimally modify the above code so it works, you're dealing with the inner-class-can-only-refer-to-finals problem. Carve out a named inner class instead of an anonymous one and create a String host field in that class. Pass that instance to invokeAndWait(). But this is still icky, in my humble opinion, and far inferior to the Future<> approach I cited above.

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());
风流物 2024-10-05 12:20:06

优雅的?使用原子变量

final AtomicReference<String> host = new AtomicReference<String>();
SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        host.set(JOptionPane.showInputDialog("Enter host name: "));
    }
});
openConnection(host.get());

Elegant? Use an atomic variable

final AtomicReference<String> host = new AtomicReference<String>();
SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        host.set(JOptionPane.showInputDialog("Enter host name: "));
    }
});
openConnection(host.get());
不语却知心 2024-10-05 12:20:06

技巧:使用数组

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?

Hack: Use an array

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?
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文