3

I'm trying to come with a solution for a thread to pause and resume exactly where it was left off.

So here's a sample code emulating my problem: 2 threads are running in the background: taskThread & busyThread. When busyThread is in system is busy area, taskThread must alt/pause immediately and resume exactly where it was left off. Example, if taskThread was paused at task C (finished) it should resume at D.

I tried to use wait, notify on taskThread but without success.

public class Test
{ 
   private Thread taskThread;
   private Thread busyThread;

public static void main(String args[]) throws Exception
{ 
    Test t = new Test();
    t.runTaskThread();
    t.runBusyThread(); 
} 


public void runTaskThread()
{
    taskThread = new Thread(new Runnable(){

        @Override
        public void run()
        {
            for (int x=0; x<100; x++)
            {
                try
                {                       
                    System.out.println("I'm doing task A for process #"+x);
                    Thread.sleep(1000);

                    System.out.println("I'm doing task B for process #"+x);
                    Thread.sleep(200);

                    System.out.println("I'm doing task C for process #"+x);
                    Thread.sleep(300);

                    System.out.println("I'm doing task D for process #"+x);
                    Thread.sleep(800);

                    System.out.println("\n\n");

                } catch (InterruptedException e)
                {                       
                    e.printStackTrace();
                }                                       
            }

        }});

    taskThread.start();
}

public void runBusyThread()
{
    busyThread = new Thread(new Runnable(){

        @Override
        public void run()
        {
            while (true)
            {
                Random rand = new Random();
                int randomNum = rand.nextInt(1000);
                if (randomNum<400)
                {
                    System.out.println("Wait...system is busy!!!");                     
                    try
                    {       //what should come here to to signal taskThread to paused
                            Thread.sleep(3000);

                             //what should come here to to signal taskThread to resume

                    } catch (InterruptedException e)
                    { 
                    }
                } else
                {
                    try
                    {
                        Thread.sleep(300);
                    } catch (InterruptedException e)
                    {
                    }   
                }                       
            }               
        }});    

    busyThread.start();
}
}
adhg
  • 10,437
  • 12
  • 58
  • 94
  • 1
    just a quick question, u want to stop a thread then start it from where it left of right? – nafas Oct 29 '15 at 15:20
  • good point. No, I wish to alt/pause (let me change the title) thanks! – adhg Oct 29 '15 at 15:22
  • what would happen if you kill the thread when it needs to be stop and create a new one when it needs to be resumed, would that be a problem? – nafas Oct 29 '15 at 15:25
  • yes, it must continue where it was left off; in the real world problem it translate to wasted time – adhg Oct 29 '15 at 15:27
  • What happens when busy thread is ready to get busy, but the task thread is in the middle of a task? –  Oct 29 '15 at 15:28
  • think about it in terms of line of code. If taskThread 'hit' the line - it should execute it and pause until busyThread is done. – adhg Oct 29 '15 at 15:30
  • What's your *real* problem you're trying to solve? As you've noticed there's no good solution for what you want here, because it's logically impossible to provide a safe implementation of it. The good thing is that I've yet to see any situation where one couldn't find a better solution that avoids the need for this. – Voo Oct 29 '15 at 17:38

4 Answers4

1

There are two very useful classes in concurrency package - CountDownLatch and CyclicBarrier. If you need this behaviour only once, you probably want the first one (as it cannot be reset).

Thread 1 will await until notified by thread 2. Once it was counted down to 0, thread 1 will never block again at await():

CountDownLatch cdl = new CountDownLatch(1);

// thread 1:
cdl.await();

// thread 2:
cdl.countDown();

Threads will block at await() until there are exactly two threads waiting:

CyclicBarrier barrier = new CyclicBarrier(2);

// both threads:
barrier.await();

EDIT:

Here is what I came up with when modifying your code, however it is unclear to me whether it is expected behaviour.

Note a volatile keyword on the CountDownLatch - it is very important here, otherwise taskThread may cache the initial object (new CountDownLatch(0)) and hence never block.

public class Test {

    private Thread taskThread;
    private Thread busyThread;

    private volatile CountDownLatch cdl = new CountDownLatch(0);

    public static void main(String args[]) throws Exception {
        Test t = new Test();
        t.runTaskThread();
        t.runBusyThread();
    }

    public void runTaskThread() {
        taskThread = new Thread(() -> {
            for (int x = 0; x < 100; x++) {
                waitIfSystemBusy();
                System.out.println("I'm doing task A for process #" + x);
                sleep(1000);

                waitIfSystemBusy();
                System.out.println("I'm doing task B for process #" + x);
                sleep(200);

                waitIfSystemBusy();
                System.out.println("I'm doing task C for process #" + x);
                sleep(300);

                waitIfSystemBusy();
                System.out.println("I'm doing task D for process #" + x);
                sleep(800);

                System.out.println("\n\n");
            }
        });

        taskThread.start();
    }

    public void runBusyThread() {
        busyThread = new Thread(() -> {
            while (true) {
                Random rand = new Random();
                int randomNum = rand.nextInt(1000);
                if (randomNum < 400) {
                    System.out.println("Wait...system is busy!!!");
                    cdl = new CountDownLatch(1); // signal taskThread to pause
                    sleep(3000);
                    cdl.countDown(); // signal taskThread to resume
                } else {
                    sleep(300);
                }
            }
        });

        busyThread.start();
    }

    private void waitIfSystemBusy() {
        try {
            cdl.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private static void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

}
Jaroslaw Pawlak
  • 5,538
  • 7
  • 30
  • 57
  • thanks Jarsolaw; question: how would you ensure that when the taskThread is at B it will resume to C after busy is finished? – adhg Oct 29 '15 at 18:34
  • @adhg What do you mean by "is at B" and "will resume to C"? – Jaroslaw Pawlak Oct 30 '15 at 16:37
  • Look at the code below, the taskThread has a for-loop with tasks (System.out...A...B...C) So when the taskThread is paused at B it should continue from the point it was left and move on to C (not A). Thanks! – adhg Oct 30 '15 at 22:35
  • @adhg I gave it a go with your code and I edited my answer. Is this what you need? – Jaroslaw Pawlak Nov 02 '15 at 17:45
  • Jaroslaw, I eventually used a semaphore (mutex) with the same design (check waifIfSysBusy after each block). Thanks for your answer. – adhg Nov 08 '15 at 02:43
0

It would be done using the deprecated methods Thread.suspend/resume. They are deprecated as they are deadlock prone, whereas concurrency mechanisms like locks behave in a designed explicit manner (but still deadlock prone).

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • mmmm, I though about it but realized they're 'old'. If so, how would you use it (where in the code?) – adhg Oct 29 '15 at 15:28
  • In the busyThread: `...; taskThread.suspend(); heavyWork(); taskThread.resume();`. Mind I have never used them, as my threads are nicer to each other. – Joop Eggen Oct 29 '15 at 15:47
  • If you have to use resume/suspend, your multi threaded application is so badly designed that it'll have a few other errors as well. There's good reason those things are deprecated - there's no sane reason to ever use them. Honestly the only good thing about these functions is that they simplify my life with code reviews: I can reject every code that uses these functions without spending extra time (yes it's that bad to use them) – Voo Oct 29 '15 at 17:15
0

I would suggest create a class that implements Runnable which simply keep track of the stages you are in

just as an example (please change accordingly)

class MyRunnable implements Runnable {
    private int stage = 0; // if you want it gloabally, then use static
    @Override
    public void run() {
        try{
            switch(stage){
                case 1:
                    System.out.println("1");
                    stage++;

                case 2:
                    System.out.println("2");
                    Thread.sleep(2000);
                    stage++;
                default:
                    stage = 0;

            }
        }catch (Exception e){

        }
    }

}

now to use such class you just need to create a new thread for example:

public static void main(String[] args) throws Exception{

  MyRunnable myRunnable=new MyRunnable();
  new Thread(myRunnable).start(); //it prints 1

  Thread.sleep(1000);

  new Thread(myRunnable).start(); //prints 2 follow by 2 sec sleep

}

NOTE:

this example wasn't intended to answer the question exactly but rather show a logic how it can be done.

EDIT 1:

what should come here to to signal taskThread to paused

taskThread.interupt();

what should come here to to signal taskThread to resume

taskThread=new Thread(myRunnable);
taskThread.start();
nafas
  • 5,283
  • 3
  • 29
  • 57
  • thanks nafas for your thoughts, I think this design will add more maintenance on my code rather then solve it with pause/continue mechanism (assuming one exists). If there isn't such mechanism, I more likely to entertain this idea. Thanks! – adhg Oct 29 '15 at 15:51
  • @adhg I found this link that might be what you are looking for mate: http://stackoverflow.com/questions/11989589/how-to-pause-and-resume-a-thread-in-java-from-another-thread – nafas Oct 29 '15 at 15:55
  • I saw this, but it doesn't actually continue from the place it was left off. It starts from the beginning. – adhg Oct 29 '15 at 16:02
  • @adhg well I'm out of cards mate, lemme know if you find an easier way mate, I like to know the answer too – nafas Oct 29 '15 at 16:05
0

Instead of sleep() I would prefer wait() and notifyAll(). have a Boolean systemBusy, implement get and set methods; now in thread1

    run(){
       synchronize(something){
          while(isSystemBusy()){
             try{
                wait();}
             catch{}
          }
       }
    }

and on the other thread

        run(){
           setSystemBusy(true);
           //piece of code
           //task finished
           notifyAll();
           setSystemBusy(false);
        }

you can use this in multiple waiting threads just remember to set appropriate while condition false after notify all.

Sulabh Deep Puri
  • 314
  • 1
  • 13