Combining two Runnable objects

2019-05-23 07:12发布

问题:

Say for example that I have a Runnable called RunnableA that does something. I also have a Runnable called RunnableB that does something else. Is there a way that I can combine these two Runnables someway so that they will run in the same thread?

The second part of the question is if this is possible, can I then specify the order that they will run in?

EDIT!: The reason why I wanted to do this was because I need to run code on the EDT but some of the other code needs to be run on another thread. Please take a look at the code below.

Something like this


public final class CompoundRunnable implements Runnable
{
    private final Iterable runnables;

    public CompoundRunnable(Iterable runnables)
    {
        // From Guava. Easy enough to do by hand if necessary
        this.runnables = Lists.newArrayList(runnables);

    }

    public CompoundRunnable(Runnable... runnables)
    {
        this(Arrays.asList(runnables));
    }

    @Override
    public void run()
    {
        for (Runnable runnable : runnables)
        {
             runnable.run();
        }
    }
}


public void setStatusAndProgress(final String status,Runnable runnable)
    {
        Runnable startUpRunner = new Runnable()
        {
            public void run()
            {
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        setStatus(status);
                        selfReference.getProgressBar().setIndeterminate(true);
                    }

                });
            }
        };
        Runnable cleanUpRunner = new Runnable()
        {
            public void run()
            {
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        setStatus("");
                        getProgressBar().setIndeterminate(false);
                    }
                });
            }
        };

        Runnable theRunner = new CompoundRunnable(startUpRunner,runnable,cleanUpRunner);
        new Thread(theRunner).start();
    }

Sorry if this isnt explained well, post comments if you need clarification.

Thanks!

回答1:

Well you can certainly create a Runnable which just runs one runnable then the other:

public final class CompoundRunnable implements Runnable
{
    private final Runnable first;
    private final Runnable second;

    public CompoundRunnable(Runnable first, Runnable second)
    {
        this.first = first;
        this.second = second;
    }

    @Override
    public void run()
    {
        first.run();
        second.run();
    }
}

More generally, you could make it take an Iterable<Runnable>, copy all the Runnable references, and then run them in order. For example:

public final class CompoundRunnable implements Runnable
{
    private final Iterable<Runnable> runnables;

    public CompoundRunnable(Iterable<Runnable> runnables)
    {
        // From Guava. Easy enough to do by hand if necessary
        this.runnables = Lists.newArrayList(runnables);
    }

    public CompoundRunnable(Runnable... runnables)
    {
        this(Arrays.asList(runnables));
    }

    @Override
    public void run()
    {
        for (Runnable runnable : runnables)
        {
             runnable.run();
        }
    }
}


回答2:

Sure. Just create another runnable that calls the run methods of your two existing Runnable's as part of its implementation. You can make the wrapping Runnable purpose-built or generic with a list of Runnables, etc.

public Runnable combineRunnables(Runnable a, Runnable b)
{
   Runnable retVal = new Runnable()
   {
        public void run()
        {
            a.run();
            b.run();
        } 
   };

   return retVal;
}

Perhaps what's confusing you is association of Runnable with threads. The Runnable interface isn't tied to threads and doesn't by itself have any threading semantics, so you can easily combine it like above without somehow getting into trouble with thread states.



回答3:

Yes you can of course combine them. In general though, there shouldn't be much inside a runnable except a call to some other object, to actually get the work done. Can you not just take the "insides" of the runnables you need and execute them in the context you want.

If you want to make sure things are simply executed one after the other, rather than at the same time you can use a SingleThreadExecutorService and give them the Runnables. It will execute one at a time.

If you really want to run multiple runnables then you can do it like this (the first RuntimeException will exit).

Note the static convenience method so you can say "new Thread(CompositeRunnable.combine(a,b,c,d....))"

public class CompositeRunnable implements Runnable {

    private final Runnable[] runnables;

    public CompositeRunnable(Runnable... runnables) {
        this.runnables = runnables;
    }

    public void run() {
        for (Runnable runnable : runnables) {
            runnable.run();
        }
    }

    public static Runnable combine(Runnable... runnables) {
        return new CompositeRunnable(runnables);
    }
}


回答4:

Another variation.

public static Runnable combineRunnables(final Runnable... runnables) {
   return new Runnable() {
        public void run() {
            for(Runnable r: runnables) r.run();
        } 
   };
}