Java 如何将EDT的结果传递回其他线程?

Java 如何将EDT的结果传递回其他线程?,java,swing,event-dispatch-thread,Java,Swing,Event Dispatch Thread,我有以下用例: 我在线程A(不是EDT)中执行代码。然后我想问用户一个问题,但这必须在EDT上完成,因为它涉及到Swing代码(打开对话框等)。最后,我想把用户的答案传递回线程A,这样它就可以继续了 我正在努力寻找一种将用户的答案传递回线程a的好方法。如何做到这一点?在线程a中,您可以使用SwingUtilities.invokeAndWait(Runnable)在EDT上执行用户提示。这将阻止线程A,直到您的runnable完成(即,直到用户提交结果并将其存储在某处)。一旦线程A重新获得控制权

我有以下用例:

我在线程A(不是EDT)中执行代码。然后我想问用户一个问题,但这必须在EDT上完成,因为它涉及到Swing代码(打开对话框等)。最后,我想把用户的答案传递回线程A,这样它就可以继续了


我正在努力寻找一种将用户的答案传递回线程a的好方法。如何做到这一点?

在线程a中,您可以使用SwingUtilities.invokeAndWait(Runnable)在EDT上执行用户提示。这将阻止线程A,直到您的runnable完成(即,直到用户提交结果并将其存储在某处)。一旦线程A重新获得控制权,您的runnable可以被写入存储结果的某个位置,以便线程A可以访问它。

基本上,您需要使用
事件队列#invokeAndWait
(又称
SwingUtilities#invokeAndWait
)。这将阻止当前线程,直到run方法返回

真正的诀窍是尝试设置它,以便获得返回值;)

在本例中,我基本上创建了自己的查询对象,实现了
Runnable
,并可以存储来自用户的响应
FutureTask dialogTask=newfuturetask(newcallable(){
FutureTask<Integer> dialogTask = new FutureTask<Integer>(new Callable<Integer>() {
  @Override public Integer call() {
    return JOptionPane.showConfirmDialog(...);
  }
});
SwingUtilities.invokeLater(dialogTask);
int result = dialogTask.get();
@重写公共整数调用(){ 返回JOptionPane.showConfirmDialog(…); } }); 调用器(dialogTask); int result=dialogTask.get();
我写了以下方便的方法来补充jtahlborn的答案。它添加了一个检查以避免阻塞EDT,并提供了一个很好的流线型异常处理:

/**
 * executes the given callable on the EDT, blocking and returning the result of the callable.call() method.
 * 
 * If call() throws an exception, it is rethrown on the the current thread if the exception is either a RuntimeException, or the
 * class that is assignable to exceptionClass. Otherwise, it is wrapped in a RuntimeException and thrown on the current thread.
 * 
 * @param exceptionClass The class of any exception that may be thrown by the callable.call() method, which will now be thrown
 *            directly by this method (ie, not wrapped in an ExecutionException)
 */
public static <T, E extends Exception> T invokeAndWaitAndReturn(Callable<T> callable, Class<E> exceptionClass)
        throws InterruptedException, E {
    if (SwingUtilities.isEventDispatchThread()) {
        try {
            return callable.call();
        }
        catch (Exception e) {
            throw throwException(exceptionClass, e);
        }
    }
    else {
        FutureTask<T> task = new FutureTask<T>(callable);
        SwingUtilities.invokeLater(task);

        try {
            return task.get();
        }
        catch (ExecutionException ee) {
            throw throwException(exceptionClass, ee.getCause());
        }
    }
}

@SuppressWarnings("unchecked")
private static <E extends Exception> E throwException(Class<E> exceptionClass, Throwable t) {
    if (exceptionClass.isAssignableFrom(t.getClass())) {
        return (E) t;
    }
    else if (t instanceof RuntimeException) {
        throw (RuntimeException) t;
    }
    else {
        throw new RuntimeException(t);
    }
}
/**
*在EDT上执行给定的可调用函数,阻塞并返回callable.call()方法的结果。
* 
*如果call()引发异常,如果异常是RuntimeException或
*可分配给exceptionClass的类。否则,它将被包装在RuntimeException中并在当前线程上抛出。
* 
*@param exceptionClass可调用的.call()方法可能引发的任何异常的类,现在将引发该异常
*直接使用此方法(即,不包装在ExecutionException中)
*/
公共静态T invokeAndWaitAndReturn(可调用可调用,类exceptionClass)
抛出中断异常,E{
if(SwingUtilities.isEventDispatchThread()){
试一试{
返回callable.call();
}
捕获(例外e){
抛出throwException(exceptionClass,e);
}
}
否则{
FutureTask任务=新的FutureTask(可调用);
调用器(任务);
试一试{
return task.get();
}
捕获(被执行者){
抛出throwException(exceptionClass,ee.getCause());
}
}
}
@抑制警告(“未选中”)
私有静态E throweException(类exceptionClass,Throwable t){
if(exceptionClass.isAssignableFrom(t.getClass())){
返回(E)t;
}
else if(运行时异常的t实例){
抛出(运行时异常)t;
}
否则{
抛出新的运行时异常(t);
}
}
您这样称呼它,不必担心您当前是否正在EDT上执行:

try {
    Integer result = invokeAndWaitAndReturn(new Callable<Integer>() {
        public Integer call() throws MyException {
            // do EDT stuff here to produce the result
        }
    }, MyException.class);
} catch(InterruptedException ie) {
    Thread.currentThread().interrupt();
} catch(MyException me) {
    // handle the "expected" Exception here
}
试试看{
整数结果=invokeAndWaitAndReturn(new Callable()){
公共整数调用()引发MyException{
//在这里做EDT来产生结果
}
},MyException.class);
}捕获(中断异常ie){
Thread.currentThread().interrupt();
}抓住(我的例外){
//在此处处理“预期”异常
}

此util方法在单独的swing线程中执行供应商中的操作,并等待响应。如果存在以下情况,它还会引发异常:

public class InvokeAndGet {

public static <T> T execute(Supplier<T> supplier, long timeout) throws InterruptedException, SyncException {
    AtomicReference<T> atomicRef = new AtomicReference<>();
    AtomicReference<Exception> atomicException = new AtomicReference<>();
    CountDownLatch latch = new CountDownLatch(1);
    SwingUtilities.invokeLater(() -> {
        try {
            atomicRef.set(supplier.get());
        }catch(Exception e){
            atomicException.set(e);
        }finally {
            latch.countDown();
        }
    });
    latch.await(timeout, TimeUnit.MILLISECONDS);
    if(atomicException.get() != null) {
        throw new SyncException(atomicException.get());
    }else {
        return atomicRef.get();
    }
}

@SuppressWarnings("serial")
public static class SyncException extends Exception {
    public SyncException(Throwable arg0) {
        super(arg0);
    }
}

}

仍然有改进的余地,使其更具通用性,因为此实现采用SwingUtilities,有时您希望使用ThreadExecutor。

好的,这正是我提出的基本方法。我用getter和setter创建了一个与您的想法非常相似的通用响应。我还必须添加异常以将错误传递回线程A。。。这看起来很严厉,但也许这是唯一的出路。。。谢谢你的时间!这也是我最好的尝试,但使用公认答案中显示的FutureTask要好得多。无论如何,谢谢!:)正确的。。。所以“将结果存储到某个地方”部分就是我要问的。。。有没有一种优雅的方法可以实现这一点?似乎FutureTask/SwingUtilities.invokeLater()和task.get()可以很好地完成这一目标,而不需要编写任何额外的代码来存储/检索线程之间的结果。这非常有趣。。。我想知道这将如何处理call()方法中抛出的异常?我希望将异常(如果有)抛出到原始(不是EDT)线程上…@EricLindauer-所有这些都会按照您的预期进行处理。异常将被抛出到包含在ExecutionException中的原始线程中(有关详细信息,请参阅
Future.get()
)。注意:调用此代码时,必须确保您不在EDT上,因为get()将永远不会返回,EDT也永远不会被释放以执行FutureTask…当然,要编写一个版本的不带异常参数或2或3的“invokeAndWait”非常简单!常用的“no Exception”方法是:public static T invokeAndWaitAndReturn(Callable Callable)抛出InterruptedException{return invokeAndWaitAndReturn(Callable,RuntimeException.class);}
public class InvokeAndGet {

public static <T> T execute(Supplier<T> supplier, long timeout) throws InterruptedException, SyncException {
    AtomicReference<T> atomicRef = new AtomicReference<>();
    AtomicReference<Exception> atomicException = new AtomicReference<>();
    CountDownLatch latch = new CountDownLatch(1);
    SwingUtilities.invokeLater(() -> {
        try {
            atomicRef.set(supplier.get());
        }catch(Exception e){
            atomicException.set(e);
        }finally {
            latch.countDown();
        }
    });
    latch.await(timeout, TimeUnit.MILLISECONDS);
    if(atomicException.get() != null) {
        throw new SyncException(atomicException.get());
    }else {
        return atomicRef.get();
    }
}

@SuppressWarnings("serial")
public static class SyncException extends Exception {
    public SyncException(Throwable arg0) {
        super(arg0);
    }
}

}
@Test
public void execute() throws InterruptedException, SyncException {
    Integer result = InvokeAndGet.execute(() -> 1+1, 5000);
    assertEquals(2, result.intValue());
}

@Test(expected = SyncException.class)
public void executeException() throws InterruptedException, SyncException {
    InvokeAndGet.execute(() -> 1/0, 5000);
}