简体   繁体   中英

Interruptable for-loops

I'm currently trying to implemenent interruptable Jobs. The job in general looks like this

public abstract class Job
{
    private boolean interruptFlag;

    public boolean isinterrupted()
    {
        return interruptFlag;
    }

    public void interrupt()
    {
        interruptFlag = true;
    }

    public abstract void execute();

}

public class JobImplementation extends Job
{
    public void execute
    {
        for (int i = 0; i < 10; i++)
        {
            doSomethingWithI(i);
            if (interruptedFlag())
            {
                break;
            }
        }
    }
}

I would like to interrupt the for-loop in the execute-body when some controller called the interrupt()-method.

But i don't want to implement this in every single for loop in every single JobImplementaionClass.

What i would like to do is something like a method in the abstract Job-class which gets the executing code and the conditions as parameter.

Something like

protected void doFor(conditionCode, executionCode)
{
    for (conditionCode)
    {
         executeCode();
         if (isInterrupted())
             break;
    }
}

and the JobImplementation-class would not use the normal for-loop but:

public void execute()
{
    doFor({int i = 0; i < 10; i++}, {doSomethingWithI(i)})
}

This way i try to hide controlling-details from the actual JobImplementation. When i write a new JobImplementation i could to use this doFor-loop and have the possibility to interrupt without having to care for it.

Does any one have an idea how to actually implement this? Is this possible?

public abstract class LoopJob extends Job {
    private int iterations;

    public LoopJob(int iterations) {
        this.iterations = iterations;
    }

    @Override public void execute() {
        for (int i = 0; i < iterations && !isInterrupted(); ++i) {
            executeIteration(i);
        }
    }

    protected abstract void executeIteration(int i);
}

Something like this? You can make it more flexible by putting the initializer, condition and incrementer into additional abstract methods, turning the loop variable into a member of the derived class, if you really need that kind of flexibility.

you could turn the for -loop into a while -loop

public void execute() {
    int i = 0;
    while (!isInterrupted() && i < 10) {
        // do something with i
        i++;
    }
}

I would suggest using an Executors.singleThreadedExecutor where you submit tasks which consist of only one iteration. In your controlling class you'd just make sure you submit the task the right number of times, and interruption would be handled by simply shutting down the ExecutorService .

There is one important thing that previous answers have not considered: concurrency

You should use something like an AtomicBoolean to control the interrupted flag (because, at the moment, when some thread 'interrupts' your loop, your loop code may never get the new interrupted state).

My suggestion would be to have code like the following:

public abstract class Job
{
    private final AtomicBoolean interruptFlag = new AtomicBoolean(false);
    private final int loopCount;

    protected Job(final int loopCount) {
        this.loopCount = loopCount;
    }

    public final boolean isinterrupted()
    {
        return interruptFlag.get();
    }

    public final void interrupt()
    {
        interruptFlag.set(true);
    }

    public final void execute() {
        for (int i = 0; i < loopCount && !checkInterrupted(); i++) {
           doSomethingWithI(i);
        }
    }

    protected abstract boolean checkInterrupted();

    protected abstract void doSomethingWithI(int i);

}

public class InterruptableJob extends Job
{
    public InterruptableJob () {
         super(10);
    }

    protected final boolean checkInterrupted() {
         return isInterrupted();
    }

    protected final void doSomethingWithI(final int i) {
          .... ;
    }
}

My reasoning for the double-access to isInterrupted (by using checkInterrupted() ) is so that, when you have a class where you do not want to interrupt it, your implementation of the loop can be:

    protected final boolean checkInterrupted() {
         return false;
    }

and with the above implementation, the Java runtime will essentially ignore the test completely and it will have no performance impact.

note how I use an AtomicBoolean to avoid any concurrency issues

Sorry, for the long wait. For those, who are still interested i now found the time to implement a solution which seems to work.

public abstract class Job
{
    AtomicBoolean isInterrupted = new AtomicBoolean(false);

    public boolean isinterrupted()
    {
        return interruptFlag;
    }

    public void interrupt()
    {
        isInterrupted.set(true);
    }

    public abstract void execute();

    public void doLoop(Callable<Boolean> callable, Runnable runnable) 
    {
    try
        {
            while (true)
        {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            Future<Boolean> result = executor.submit(callable);
                if (!result.get())
                break;
                executor.submit(runnable);
            if (isInterrupted.get())
                break;

                        // TestCode. simulates some serious code given 
                        // by the the runnable
               try
               {
                   Thread.sleep(1000);
               } 
                               catch (InterruptedException e)
               {
                           // do Something
               }
        }
    }
    catch (InterruptedException e)
    {
            // do Something
    } 
    catch (ExecutionException e)
    {
            // do Something
    }
}


}

And the implementing SubClass goes

public class JobImplementation extends Job
{
public void execute()
{
    Thread interruptor= new Thread(new Interrupter(this));
    interruptor.start();
    final Iterator<String> it = collection.iterator();
    doLoop(new Callable<Boolean>() 
           {
               public Boolean call() throws Exception 
               {
                   return it.hasNext();
               }}, 
           new Runnable() 
           {
               public void run()
               {
                   System.out.println(it.next());
               }
           });
}


public class Interrupter implements Runnable 
    {
        LoopUserClass user;
        public Interrupter(LoopUserClass user)
        {
            this.user = user;
        }

        public void run() 
        {
            try
            {
                Thread.sleep(5000);
            } 
            catch (InterruptedException e)
            {
                // do Something
            }
            user.interrupt();
        }
}

    public static void main(String[] args)
    {
        LoopUserClass user = new LoopUserClass();       
        for (int i = 0; i < 10; i++)
        {
            user.collection.add("testString" + i);
        }
        user.execute();
    }
}

The implementing subClass now uses the doLoop(Callable, Runnable)-method. The doLoop-method runs a endless-while. The Callable calculates the break-condition. The runnable executes the actual code of your loop-body. Afterwards doLoop checks if the isInterrupted-flag is true and breaks in that case.

The sleep in the doLoop is just for the test. It did not seem to work, if i put the sleep into the runnable, but pffft. just testCode... same goes for the inner Class Interruptor. you should imagine, that the interrupt will come from somewhere completely else.

This concept still looks a bit ugly, since the user of the doLoop has to create the Callable and Runnable-Objects. That will probably not change before closures are implemented in java.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM