如何在没有*忙等待的情况下读取SwingWorker的结果*?

时间:2011-09-08 08:55:07

标签: java swing swingworker

我正在编写一个使用SwingWorker执行文件菜单操作的应用程序。每个被调用的方法返回一个boolean值,告诉操作是否成功执行。

目前我忙于等待结果,如下:

public boolean executeOperation() {
    final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
        @Override
        protected Boolean doInBackground() throws Exception {
            // ..

            if (aborted) {
                return false;
            }

            // ..

            return true;
        }
    };

    worker.execute();

    // busy wait
    while (!worker.isDone())
        ;

    try {
        return worker.get().booleanValue();
    } catch (Exception e) {
        // handle exceptions ..
        return false;
    }
}

是否有一种较少投票的方式来解决这个问题?

直接使用worker.get()将无效,因为它会阻止EDT,等待任务完成 - 这意味着即使我在SwingWorker内打开的对话框也不会被绘制。

编辑:如果可能的话,我想避免方法(或工人)异步地传达他们的结果。我正在实现几个相互依赖的简短方法(文件 - &gt;打开,新建,关闭,保存,另存为,退出)(即当尝试退出,退出呼叫关闭,关闭可能会调用保存,保存可能会调用另存为)。异步解决这个问题似乎使代码更加复杂。

5 个答案:

答案 0 :(得分:6)

SwingWorker的目的正是在后台启动一些任务,而不是阻止EDT。要么你想要同步的东西,要么你想要的东西都会阻止EDT,或者你想要异步的东西,后台任务应该使用SwingWorker的publish方法更新它的状态。

您可以在任务运行时显示带有进度条的阻止模式对话框,并在任务完成后隐藏它。

替代方法是阻止一段时间,希望任务快速完成,然后备份到异步方式。这可以使用get method taking a timeout作为参数来完成。

答案 1 :(得分:4)

您可以使用异步范例。查看Observer / Observable并使用该作业将结果传回给当前正在进行轮询的对象。

答案 2 :(得分:4)

  

直接使用worker.get()将无效,因为它会阻止EDT,等待任务完成 - 这意味着即使我在SwingWorker内打开的对话框也不会被绘制。

他们也没有使用当前代码。忙碌的等待会像调用worker.get()那样阻塞EDT - 只有一个事件调度线程,如果该线程正在循环旋转或等待一个线程,SwingWorker中的对话框就会被阻止锁。这里的问题是,如果一个方法在EDT上运行,它就不能从异步操作(不占用EDT)返回一个值给它的调用者。

对完成的异步处理作出反应的正确方法是覆盖SwingWorker中的done()方法。

另请查看http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html了解详情。

答案 3 :(得分:4)

上面几个人提到的一种方法是覆盖SwingWorker的done方法。但是,如果出于某种原因,您希望在SwingWorker之外和调用代码中发布SwingWorker代码,则可以利用SwingWorker的属性更改支持。只需将一个PropertyChangeListener添加到SwingWorker并侦听属性名称为“state”的state属性。然后,您可以使用其getState()方法提取SwingWorker的状态。完成后,它将返回SwingWorker.StateValue枚举的DONE值。例如(根据我在another thread给出的答案,在SO上):

  if (turn == white) {
     try {
        final SwingWorker<Move, Void> mySwingWorker = new SwingWorker<Move, Void>() {
           @Override
           protected Move doInBackground() throws Exception {
              Engine e = new Engine(); // Engine is implemented by runnable
              e.start();
              Move m = e.getBestMove(board);                  
              return m;
           }
        };

        mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() {
           public void propertyChange(PropertyChangeEvent evt) {
              if (StateValue.DONE == mySwingWorker.getState()) {
                 try {
                    Move m = mySwingWorker.get();

                    // TODO: insert code to run on the EDT after move determined

                 } catch (InterruptedException e) {
                    e.printStackTrace();
                 } catch (ExecutionException e) {
                    e.printStackTrace();
                 }
              }
           }
        });
        mySwingWorker.execute();

     } catch (Exception e) {
        e.printStackTrace();
     }
  }

答案 4 :(得分:0)

当我想要一个函数返回一个可以在swing工作者中计算的值时,我遇到了类似的问题。我不想简单地让那个线程阻止EDT。我也不想阻止它。所以我使用了这样的信号量:

public boolean executeOperation() {
    final Semaphore semaphore = new Semaphore(1);
    semaphore.acquire(1); // surround by try catch...
    final SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
        @Override
        protected Boolean doInBackground() throws Exception {
            // ..

            if (aborted) {
                semaphore.release();
                return false;
            }

            // ..
            semaphore.release();
            return true;
        }
    };

    worker.execute();



    try {
        semaphore.tryAcquire(1, 600, TimeUnit.SECONDS); // awakes when released or when 10 minutes are up.
        return worker.get().booleanValue(); // blocks here if the task doesn't finish in 10 minutes.
    } catch (Exception e) {
        // handle exceptions ..
        return false;
    }
}

我想这对所有情况都不理想。但我认为这是一种对我非常有用的替代方法。