Wednesday 16 May 2012

Pause and Resume Thread


If you want a single thread that can do start, pause, resume and stop followings  abstract class you need to extend is tested solution for you. Extend  this class, define what task you want to run in thread and it will give you methods to handle different thread life cycle stages. Please let me know if you find anything wrong :)

IMP NOTE: Remember once you call the shutdown/shutdownNow method on executor, any further attempt to submit task will throw RejectedExecutionException.

1. Pausing Normal Thread.run:


package co.rnd.thread.life;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import com.google.common.util.concurrent.Monitor;

public abstract class PausableTask implements  Runnable{

private ExecutorService executor = Executors.newSingleThreadExecutor();
private Future<?> publisher;
protected volatile int counter;
private void someJob() {
System.out.println("Job Done :- " + counter);

}

abstract void task();

@Override
public void run() {
while(!Thread.currentThread().interrupted()){
task();
}
}

public void start(){
publisher = executor.submit(this);
}

public void pause() {
counter = 100;
publisher.cancel(true);
}

public void resume() {
counter = 200;
start();
}

public void stop() {
counter = 300;
executor.shutdownNow();
}
}

2. Pausing Scheduled Thread:
package co.rnd.thread.life;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public abstract class PausableScheduledTask
{
   private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
   private volatile boolean isPaused = false;

   abstract void task();

   public void start()
   {

         if(executor == null || executor.isShutdown())
   {
   executor = Executors.newSingleThreadScheduledExecutor();
     final Runnable beeper = new Runnable()
      {
         @Override
         public void run()
         {
            if (!isPaused)
               task();
         }
      };
      executor.scheduleAtFixedRate(beeper, getInitialDelay(), getPeriodDelay(), TimeUnit.SECONDS);
   } else
   {
   resume();
   }

     
   }

   public void pause()
   {
      isPaused = true;
   }

   public void resume()
   {
      isPaused = false;
   }

   public void stop()
   {
      executor.shutdownNow();
      executor = null;
   }

   public long getInitialDelay()
   {
      return 0;
   }

   public long getPeriodDelay()
   {
      return 0;
   }

   public TimeUnit getTimeUnit()
   {
      return TimeUnit.SECONDS;
   }
}

3 comments:

  1. Hello
    Its good to see the code , but it has 1 bug
    if(executor == null || executor.isShutdown())
    {
    executor = Executors.newSingleThreadScheduledExecutor();
    } else
    {
    resume();
    }

    In above code the control never goes inside "if" statement
    In scenario
    start 1st time
    pause
    start 2nd time

    two different executors are scheduled to run
    so whatever you write in task() function gets executed twice after second start
    try running your PausableScheduledTask by putting some print stmt inside task() function and run the above scenario from main class

    Attaching the modified code

    public class PausableScheduledTask {

    private ScheduledExecutorService executor = null;
    private volatile boolean isPaused = false;
    public void task(){
    System.out.println("Hello world !!!!");
    }
    Runnable scheduler = new Runnable() {
    @Override
    public void run() {
    if (!isPaused)
    task();
    }
    };
    public void start() {

    if (executor == null || executor.isShutdown()) {
    System.out.println("Restarting");
    executor = Executors.newSingleThreadScheduledExecutor();
    executor.scheduleAtFixedRate(scheduler, getInitialDelay(), getPeriodDelay(), TimeUnit.SECONDS);
    } else {
    System.out.println("RESUMED IN START");
    resume();
    }


    System.out.println("i am here");
    }

    public void pause() {
    System.out.println("its paused");
    isPaused = true;
    }

    public void resume() {
    System.out.println("its resumed");
    isPaused = false;
    }

    public void stop() {
    executor.shutdownNow();
    executor = null;
    }

    public long getInitialDelay() {
    return 0;
    }

    public long getPeriodDelay() {
    return 2;
    }

    public TimeUnit getTimeUnit() {
    return TimeUnit.SECONDS;
    }
    }


    ReplyDelete
  2. Main method


    package com.alu.scheduler;

    public class TestScheduler {


    public static void main(String[] args) {

    PausableScheduledTask fileSyncScheduler = new PausableScheduledTask();
    fileSyncScheduler.start();
    try {
    Thread.sleep(10000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    fileSyncScheduler.pause();
    try {
    Thread.sleep(10000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    fileSyncScheduler.start();
    try {
    Thread.sleep(10000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    fileSyncScheduler.stop();
    }

    }

    ReplyDelete
    Replies
    1. Hi Ashish, Thanks for your efforts but multiple instance of beeper or scheduler is caused by your twice call to start(). In your test you should have called start(), pause() then resume() instead of start(). Anyway I have modified the code to handle this scenario as well. Thanks for comment

      Delete