Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative to Thread.suspend() and .resume()

I have a large segment of code that is not a loop, just a list of commands that happens once but takes some time. I need it to either pause or terminate this at any point based on a changing boolean value. I could use a different thread to suspend, resume and stop this code, but those methods are deprecated, so I would like to avoid using them. I could check the boolean between every line of code, but I am hoping for a more elegant solution. Is there a good way to do this?

like image 808
yesennes Avatar asked Mar 08 '15 01:03

yesennes


4 Answers

There is an excellent documentation about why Thread.stop(), Thread.pause() and Thread.resume() are deprecated:

Java Thread Primitive Deprecation

There is also an alternative for Thread.pause() / Thread.resume() using wait and notify explained.

like image 86
isnot2bad Avatar answered Oct 23 '22 11:10

isnot2bad


The correct way to handle interrupting a thread (in this case, to pause or stop it) is, naturally, with Thread#interrupt(). It is designed so that you can define safe points at which the thread can be interrupted, which for you is naturally the point between each task. So, to avoid having to manually check your variable between each task, and to be able to easily resume where you left off, you can store your tasks as a list of Runnables, and remember your position in the list from when you left off, like this:

public class Foo {
    public static void runTask(Runnable task) throws InterruptedException {
        task.run();
        if (Thread.interrupted()) throw new InterruptedException();
    }
    Runnable[] frobnicateTasks = new Runnable[] {
        () -> { System.out.println("task1"); },
        () -> { Thread.currentThread().interrupt(); }, //Interrupt self only as example
        () -> { System.out.println("task2"); }
    };
    public int frobnicate() {
        return resumeFrobnicate(0);
    }
    public int resumeFrobnicate(int taskPos) {
        try {
            while (taskPos < frobnicateTasks.length)
                runTask(frobnicateTasks[taskPos++]);
        } catch (InterruptedException ex) {
        }
        if (taskPos == frobnicateTasks.length) {
            return -1; //done
        }
        return taskPos;
    }
    public static void main(String[] args) {
        Foo foo = new Foo();
        int progress = foo.frobnicate();
        while (progress != -1) {
            System.out.println("Paused");
            progress = foo.resumeFrobnicate(progress);
        }
        System.out.println("Done");
    }
}
-->
task1
Paused
task2
Done
like image 25
Vitruvie Avatar answered Oct 23 '22 13:10

Vitruvie


I could check the boolean between every line of code, but I am hoping for a more elegant solution. Is there a good way to do this?

Unfortunately, no.

To replace pause / resume, what you really need is for one thread to "pause" another thread, without involving the code of the second thread.

This is not possible to do safely in Java as currently spec'd and implemented.

The deprecated Thread methods are the only way for one thread to kill, stop / pause, resume another thread ... if the other thread is not actively cooperating. They were deprecated because (in a nutshell) that kind of control cannot be implemented safely within any current generation mainstream JVM1.

The best you could do is wrap the pause / resume logic in a method that the second thread calls at appropriate points. (This probably should use wait / notify ... or equivalent ... to deal with the resumption. But wait/notify per se does not address your clear requirement for pause/resuming a non-cooperating thread.)


1 - You'd need something like the Isolates API to do this. There have been attempts to implement it in research projects, but AFAIK Isolates has never been implemented in a production JVM.

like image 26
Stephen C Avatar answered Oct 23 '22 13:10

Stephen C


I have an idea how to do this using AspectJ. The solution I am outlining below will allow you to pause/resume another thread. This pause will only come into effect on the next method invocation from inside of your target method after you called the pause routine. This will not require boolean checks after every line of code of your target method. I am using wait/notify to do this.

I am using Spring in the below example.

First, wrap your target method in a Runnable.

   package com.app.inter.thread.communication.runnables;


    public class TestRunnable implements Runnable{

        public void run() {
            long i=0;
            //For explanatory purposes, I have used an infinite loop below
            //and printed an increasing number

            while(!Thread.interrupted()){
                //someService.action1();
                //someService.action2();
                //....                    
                if(i++%10000000==0)
                    System.out.println("Working "+i);
            }

        }

    }

Next, you make an aspect which would be called conditionally on any method invocation inside your target method.

@Aspect
@Component
public class HandlerAspect {

    public static volatile boolean stop=false;
    public static volatile String monitor="";



    @Pointcut("withincode(public void com.app.inter.thread.communication.runnables.TestRunnable.run()) "
            + "&& call(* *.*(*)) "
            + "&& if()")
    public static boolean stopAspect(){
        return stop;
    }
    @Before("stopAspect()") 
    public void beforeAdvice(JoinPoint jp) {
       try {
           System.out.println("Waiting");

           synchronized(monitor){
               monitor.wait();
           }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        stop=false;
    }
}

Now, whenever you want to pause your thread, just set the value of stop to true, and vice versa.

@Service
public class Driver {
    @Autowired
    private HandlerAspect aspect;

    @PostConstruct
    public void init(){
        Scanner scanner = new Scanner(System.in);
        int i=-1;
        TestRunnable runnable = new TestRunnable();
        Thread thread= new Thread(runnable);
        thread.start();
        aspect.monitor="MONITOR";
        while((i=scanner.nextInt())!=0){
            switch(i){
                case 1: 
                    aspect.stop=true;
                    break;      
                case 2:

                    aspect.stop=false;                  
                    synchronized(aspect.monitor){
                        aspect.monitor.notify();
                    }
                    break;  
            }
        }
        // in case the main thread is stopped
        // while the other thread is in wait state
        synchronized(aspect.monitor){
            aspect.monitor.notify();
        }
        thread.interrupt();

    }
}

The output is as follows:

...
Working 400000001
1Working 410000001
Working 420000001
Working 430000001
Working 440000001
Working 450000001
Working 460000001
Working 470000001
Waiting
2
Working 480000001
Working 490000001
0

Working code is at https://github.com/zafar142007/InterThreadCommunication

like image 28
zafar142003 Avatar answered Oct 23 '22 11:10

zafar142003