1
\$\begingroup\$

I am trying to stop an executing runnable instance after the run method has been called.

I have come up with the below approach

    public class StoppableWorkflowTask implements Runnable {
        volatile Thread runner = null;

        @Override
        public void run() {
            runner = Thread.currentThread();
            try {

                while (true) {
                    System.out.println("Stop Thread " + Thread.currentThread().getId());
                }

            } catch (Exception ex) {
                ex.printStackTrace();
            }

        }

        void stop() {
            runner.stop();
        }

    }

public class StoppableWorkflowTaskTest {
    public static void main(String[] args) {
        StoppableWorkflowTask stoppableTask = new StoppableWorkflowTask();

        Thread thread = new Thread(stoppableTask);

        thread.start();
        System.out.println(thread.currentThread().getId());

        try {
            stoppableTask.stop();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}

As per Java documention it is unsafe to call thread.stop(). I have checked few examples , but all either use thread.sleep() which can be terminated on interrupt.

\$\endgroup\$

2 Answers 2

4
\$\begingroup\$

The recommended way from Oracle is Thread.interrupt(). For example:

public void stop() {
    Thread thread = runner;
    runner = null;
    thread.interrupt();
}

public void run() {
    runner = Thread.currentThread();
    while (runner != null) {
        System.out.println("Stop Thread " + Thread.currentThread().getId());
    }
    System.out.println(" Done ");
}

It is true that sleep() can be interrupted (which will raise an InterruptedException), which is what the interrupt() will do. However, afterwards the Thread can continue doing whatever it wants after being “interrupted”, so you must also check for a stop-condition of some kind. Above, we check runner != null. But as you can see above, you don’t need a sleep() in the worker thread.

If you don’t want to run the Thread in a loop, or you have many different loops where the task may spin, you will have to make the stop check in each of those places.

\$\endgroup\$
1
\$\begingroup\$

With the Thread stop method deprecated a thread should be stopped testing a variable and end its run according to stop method deprecated message. To avoid testing anything an alternative to an indefinite running thread implemented using an infinite loop could be having two instances with two strategies to call each other that to be stopped has just to change the calling strategy...

public class Runner implements Runnable {

    private static final int RUNNING = 0;
    private static final int RAN = 1;
    
    private long runIntervalMillis;
    private Runnable runee;
    private Paddler paddler;
    private int eventIndex;
    private Runnable[] events;

    public Runner(long runIntervalMillis) {
        this.runIntervalMillis = runIntervalMillis;
        this.runee = () -> {};
        this.paddler = new Paddler();
        this.events = new Runnable[] { () -> { this.eventIndex++; this.start(); }
                                     , () -> {} };
        this.eventIndex = RUNNING;
    }
    
    public void run() {

        Runnable scheduled = this.runee;
        Runnable runner = () -> {
            try {
                Thread.sleep(this.runIntervalMillis);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            scheduled.run();
            this.paddler.run();
        };
        this.paddler.schedule(runner);
        runner.run();
    }

    public void start() {
        Thread runner = new Thread(this);
        runner.setDaemon(true);
        runner.start();
    }

    public void schedule(Runnable runee) { this.runee = runee; }

    public void off() { this.eventIndex = RAN; this.paddler.off(); }

    public void on() { this.events[this.eventIndex].run(); }
    
    private static class Paddler implements Runnable {

        static final Runnable EMPTY = () -> {};
        
        private Runnable runner;
        
        Paddler() { this.runner = EMPTY; }
        
        @Override
        public void run() { this.runner.run(); }
        
        void schedule(Runnable runner) { this.runner = runner; }
        
        void off() { this.runner = EMPTY; }
    }
}

It runs, it can be tested...

import java.util.concurrent.atomic.AtomicReference;

public class RunnerTest {
    public static void main(String[] args) {
        Runner runner = new Runner(5000);
        final AtomicReference<Integer> index = new AtomicReference<>(0);
        runner.schedule(() -> {
            System.out.println("running...");
            int i = index.get();
            index.compareAndSet(i, ++i);
        });
        runner.start();

        try {
            Thread.currentThread().sleep(16000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        runner.off();

        try {
            Thread.currentThread().sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("index: " + index.get());
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.