【问题标题】:Scheduling Swingworker threads调度 Swingworker 线程
【发布时间】:2009-05-27 14:00:29
【问题描述】:

我的 Swing 应用程序中有 2 个进程要执行,一个用于填充列表,一个用于对列表中的每个元素进行操作。我刚刚将这 2 个进程移到 Swingworker 线程中,以在执行任务时停止 GUI 锁定,并且因为我需要对多个列表执行这组操作,所以首先并发并不是一个坏主意地方。然而,当我刚跑的时候

fillList.execute();
doStuffToList.execute();

在空列表上运行的 doStuffToList 线程 (duh...)。如何告诉第二个进程等到第一个进程完成?我想我可以将第二个进程嵌套在第一个进程的末尾,但我不知道,这似乎是一种不好的做法。

【问题讨论】:

标签: java swing concurrency scheduling swingworker


【解决方案1】:

我认为这样的事情可以做到吗?

boolean listIsFull=false;
class FillListWorker extends SwingWorker<Foo,Bar>
{
    ...
    protected void done()
    {
        synchronized (listYouveBeenFilling)
        {
            listIsFull=true;
            listYouveBeenFilling.notifyAll();
        }
    }
    ...
}

class DoStuffToListListWorker extends SwingWorker<Foo,Bar>
{
    ...
    protected Foo doInBackground()
    {
        synchronized (listYouveBeenFilling)
        {
            while (!listIsFull)
            {
                try
                {
                    listYouveBeenFilling.wait();
                }
                catch (InterruptedException ie)
                {
                    // Don't worry, we'll just wait again
                }
            }
        }
    }
    ...
}

【讨论】:

  • 我最喜欢这个发布的解决方案,但它对我不起作用,因为listIsFull 必须是最终的,因为它是从内部类访问的......这使得它不可能修改 later 的值,就像您尝试做的那样。
  • 如果你在一个方法中定义它,它只需要是final。如果你把它定义为外部类的成员字段,一切都会好起来的。
【解决方案2】:

如何告诉第二个进程等到第一个进程完成?我想我可以将第二个进程嵌套在第一个进程的末尾,但我不知道,这似乎是一种不好的做法。

您是否考虑过使用可调用对象和期货?它们听起来很适合这类事情(让 doStuffToList 在 Future.get() 而不是实际列表上工作,所以它会在调用 get 时准备好),除了整个 swingworker 业务......(将此视为建议而不是答案)

【讨论】:

  • 这可能正是我想要的,我正在研究它,谢谢
【解决方案3】:

我们有这样的东西:

private SwingWorkerExecutor swingWorkerExecutor;

//...

protected void runChain(List<SwingWorker<Void>> chainWorkers,
                        final SwingWorkerExecutor.RunAfter<Void> runAfter,
                        final SwingWorkerExecutor.RunOnError runOnError)
{
    final List<SwingWorker<Void>> remainingWorkers =
        chainWorkers.subList(1, chainWorkers.size());
    SwingWorkerExecutor.RunAfter<Void> chainRunAfter;
    if (chainWorkers.size() > 1)
    {
        chainRunAfter = new SwingWorkerExecutor.RunAfter<Void>()
        {
            @Override
            public void run(Void value)
            {
                runChain(remainingWorkers, runAfter, runOnError);
            }
        };
    }
    else
    {
        chainRunAfter = runAfter;
    }

    currentWorker = chainWorkers.get(0);

    swingWorkerExecutor.execute(currentWorker, chainRunAfter, runOnError);
}

这很简单,IMO,因为在我们的例子中 SwingWorkerExecutor 实际上包含所有难以理解的东西:

public class DefaultSwingWorkerExecutor implements SwingWorkerExecutor
{
    @Override
    public <T> void execute(SwingWorker<T, ?> worker, RunAfter<T> after,
                            RunOnError onError)
    {
        worker.addPropertyChangeListener(
            new RunAfterHandler<T>(worker, after, onError));
        worker.execute();
    }

    private static class RunAfterHandler<T> implements PropertyChangeListener
    {
        private final SwingWorker<T, ?> worker;
        private final RunAfter<T> after;
        private final RunAfter<Throwable> onError;

        protected RunAfterHandler(SwingWorker<T, ?> worker, RunAfter<T> after,
                                  RunOnError onError)
        {
            this.worker = worker;
            this.after = after;
            this.onError = onError;
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt)
        {
            if ("state".equals(evt.getPropertyName()) &&
                evt.getNewValue() == SwingWorker.StateValue.DONE)
            {
                if (worker.isCancelled())
                {
                    return;
                }

                try
                {
                    after.run(worker.get());
                }
                catch (InterruptedException e)
                {
                    Thread.currentThread().interrupt();
                }
                catch (ExecutionException e)
                {
                    onError.run(e);
                }
            }
        }
    }
}

有一些缺少的接口,应该很直接地编写而不在这里看到它们。

我们真正的部署 SwingWorkerExecutor 使用注入的 ExecutorService 而不是默认的执行(这减少了单个应用程序所需的线程池数量。)但是我们引入 SwingWorkerExecutor 的真正原因是它简化和标准化了 SwingWorker 的处理成功和错误条件,还允许替换单元测试的逻辑(我相信你知道,如果它们是单线程的,它会简单得多。)正如你所看到的,你通常需要一堆样板文件每个 SwingWorker 都在 done() 中,所以我们不这样做,而是将 done() 工作移到回调中。

附带的好处是,像在一个链中运行多个 Swing 工作者这样的事情变得非常容易实现。

【讨论】:

    【解决方案4】:

    要按顺序执行两个过程,传统上您只需调用一个方法,然后调用另一个方法(!)。

    fillList();
    doStuffToList();
    

    或者可能是这样的:

    doStuffToList(fillList());
    

    如果您一次处理一个,您可能希望两个线程之间有一个BlockingQueue。您可以通过多个 do-stuff 线程走得更远。

    就 AWT 事件调度线程 (EDT) 而言,它只是在没有阻塞的情况下分离出一个操作,稍后会收到通知。

    【讨论】:

    • 这忽略了 SwingWorker#execute 立即返回的点。 OP 清楚地知道如何调用一个又一个函数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多