6

我有一个SwingWorker调用一些不检查线程中断的代码。在调用 之后worker.cancel(true),该worker.get()方法将立即抛出CancellationException(正如它应该的那样)。然而,由于后台任务的代码从不检查它的线程是否被中断,它很高兴地继续执行。

是否有等待后台任务实际完成的标准方法?我正在寻找显示“取消...”消息或类似的内容并阻止,直到任务终止。(我敢肯定,如有必要,我总是可以在工人阶级中使用标志来完成此任务,只是寻找任何其他解决方案。)

4

3 回答 3

3

我玩了一下这个,这就是我想出的。我正在使用 aCountDownLatch并且基本上将其await()方法公开为我的SwingWorker对象上的方法。仍在寻找更好的解决方案。

final class Worker extends SwingWorker<Void, Void> {

    private final CountDownLatch actuallyFinishedLatch = new CountDownLatch(1);

    @Override
    protected Void doInBackground() throws Exception {
        try {
            System.out.println("Long Task Started");

            /* Simulate long running method */
            for (int i = 0; i < 1000000000; i++) {
                double d = Math.sqrt(i);
            }

            return null;
        } finally {
            actuallyFinishedLatch.countDown();
        }
    }

    public void awaitActualCompletion() throws InterruptedException {
        actuallyFinishedLatch.await();
    }

    public static void main(String[] args) {
        Worker worker = new Worker();
        worker.execute();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {

        }

        System.out.println("Cancelling");
        worker.cancel(true);

        try {
            worker.get();
        } catch (CancellationException e) {
            System.out.println("CancellationException properly thrown");
        } catch (InterruptedException e) {

        } catch (ExecutionException e) {

        }

        System.out.println("Awaiting Actual Completion");
        try {
            worker.awaitActualCompletion();
            System.out.println("Done");
        } catch (InterruptedException e) {

        }
    }

}
于 2010-08-19T17:25:08.923 回答
1

最接近标准的或现成的方法是提供的progress属性和/或发布/处理方法对SwingWorker。您可以在方法结束时将其设置为“我已完成”值,以指示后台工作已完成。等待 swing worker 的线程可以发出“Canceling...”消息并定期检查进度以查看是否已完成。如果等待线程是 swing EDT,那么您将需要使用 Timer 定期检查进度属性并在完成后清除取消消息。

这是一些运行顽固后台线程的示例代码,该线程被取消,然后等待直到进度达到 100。

@Test
public void testSwingWorker()
{
    SwingWorker worker = new SwingWorker() {

        @Override
        protected void process(List chunks)
        {
            for (Object chunk : chunks)
            {
                System.out.println("process: "+chunk.toString());
            }
        }

        @Override
        protected void done()
        {
            System.out.println("done");
        }

        @Override
        protected Object doInBackground() throws Exception
        {
            // simulate long running method
            for (int i=0; i<1000000000; i++)
            {
                double d = Math.sqrt(i);
            }
            System.err.println("finished");
            publish("finished");
            setProgress(100);
            return null;
        }
    };
    Thread t = new Thread(worker);
    t.start();

    try
    {
        worker.get(1, TimeUnit.SECONDS);
    }
    catch (InterruptedException e)        {
    }
    catch (ExecutionException e)        {
    }
    catch (TimeoutException e)        {
    }

    worker.cancel(true);

    // now wait for finish.
    int progress = 0;
    do
    {
        try
        {
            Thread.sleep(1000);
        }
        catch (InterruptedException e)
        {
        }
        progress = worker.getProgress();
        System.out.println(String.format("progress %d", progress));
    }
    while (progress<100);
}

另一种方法是使用publish\process方法对将指示后台线程已完成的特殊值推送到 EDT。然后,您process在 SwingWorker 中的覆盖方法会选择此特殊值并隐藏“Canceling...”消息。这样做的好处是不需要轮询或计时器。示例代码显示,虽然done在任务取消后立即调用,但即使任务取消,发布/处理方法对仍然有效。

于 2010-08-19T15:03:30.533 回答
1

受到 Paul Blessing 解决方案的启发,我对其进行了一些改进以成为一个类,您可以子类化以获得所需的功能:

class AwaitingWorker<T,V> extends SwingWorker<T, V> {

    private final CountDownLatch actuallyFinishedLatch = new CountDownLatch(1);

    /**
     * Override this to do something useful
     */
    protected abstract void performOperation();

    @Override
    protected final T doInBackground() throws Exception {
        try {
            return performOperation();
        } finally {
            actuallyFinishedLatch.countDown();
        }
    }

    public void awaitActualCompletion() throws InterruptedException {
        actuallyFinishedLatch.await();
    }

}
于 2011-06-16T17:37:46.290 回答