Scheduling Swingworker threads

折月煮酒 提交于 2019-12-06 04:09:45

Something like this would do it, I think?

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

How do I tell the second process to wait until the first one is done? I suppose I could just nest the second process at the end of the first one, but i dunno, it seems like bad practice.

Have you looked into using callables & futures instead? They sound like a good match for this sort of thing (letting the doStuffToList work on a Future.get() instead of the actual list, so it'll be ready when get is called), apart from the whole swingworker business.. (Consider this a suggestion rather than an answer)

We have something like this:

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

This is pretty simple, IMO, because in our case the SwingWorkerExecutor actually contains all the hard to understand stuff:

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

There are some missing interfaces which should be pretty straight-forward to write without seeing them here.

Our real deployment SwingWorkerExecutor executes using an injected ExecutorService instead of the default one (this reduces the number of thread pools we need for a single app.) But the real reason we introduced SwingWorkerExecutor was that it simplifies and standardises the handling of SwingWorker success and error conditions and also allows replacing the logic for unit tests (which, as I'm sure you know, are a lot simpler if they are single threaded.) As you can see there is a bunch of boilerplate you would normally need for every single SwingWorker inside done(), so instead of doing that, we move the done() work into a callback.

The side-benefit is that things like running multiple Swing workers in a chain become pretty easy to implement.

To perform two processes sequentially, traditionally you just call one method after the other(!).

fillList();
doStuffToList();

Or perhaps something like:

doStuffToList(fillList());

If you are processing one at a time, you might want two threads with a BlockingQueue between. You might go further by having multiple do-stuff threads.

As far as the AWT Event Dispatch Thread (EDT) is concerned it's just spun off an action without blocking, and will get notified later.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!