4

Is it possible to rewrite this code for better working with processor? I have a class, which does some tasks with fixed periodicy in a separate thread. Sometimes this process can be paused and resumed. Currently I am using a flag for pausing, it works fine, but loop in this way still loads processor when process is paused. Is it possible to fix this?

private boolean mIsCanceled = false;
private boolean mIsPaused = true; // TODO more efficient for processor way of pausing is required
private final Thread mTimerThread = new Thread(new Runnable() {
    @Override
    public void run() {
        while(!mIsCanceled){
            try {
                Thread.sleep(UPDATE_PERIOD);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (!mIsPaused){
                doStep();
            }
        }
    }
});

public MyClass(){
    mTimerThread.start();
}

private void pause(){
    mIsPaused = true;
}

private void resume(){
    mIsPaused = false;
}

private void doStep(){
    // Some code
}

Please just provide alternative implementation of my code.

P.S. The environment is Android OS 2.2+

Rajesh J Advani
  • 5,585
  • 2
  • 23
  • 35
Solvek
  • 5,158
  • 5
  • 41
  • 64
  • 1
    Increasing your `UPDATE_PERIOD` will decrease the CPU load. – Sergey Kalinichenko May 19 '12 at 14:01
  • Why do you say "loop in this way still loads processor when process is paused"? Thread.sleep() is supposed to relinquish execution for the given time, and so NOT 'load the processor' during that time. – Rajesh J Advani May 19 '12 at 14:06
  • 1. Increasing your UPDATE_PERIOD does not work for me 2. I would prefer my task wait until class is paused instead of checking the flag and executing the loop (even if I have a sleep in the loop) – Solvek May 19 '12 at 14:15

4 Answers4

6

The tools available are:

wait/notify - we are all trying to get away from this archaic system.

Semaphores - once your thread has grabbed it you hold it until release so grabbing it again does not block. This means you cannot pause from within your own thread.

CyclicBarrier - Must be created anew each time it is used.

ReadWriteLock - My favorite. You can have as many threads pausing you as you like and you will only resume when all of them have called resume. You can even pause yourself if you wish.

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * PauseableThread is a Thread with pause/resume and cancel methods.
 *
 * The meat of the process must implement `step`.
 *
 * You can either extend this and implement `step` or use the factory.
 *
 * Note that I cannot extend Thread because my resume will clash with Thread's deprecated one. 
 *
 * Usage: Either write a `Stepper` and run it in a `PausableThread` or extend `PausableThread` and call `blockIfPaused()` at appropriate points.
 */
public abstract class PauseableThread implements Runnable {
  // The lock.
  // We'll hold a read lock on it to pause the thread.
  // The thread will momentarily grab a write lock on it to pause.
  // This way you can have multiple pausers using normal locks.
  private final ReadWriteLock pause = new ReentrantReadWriteLock();
  // Flag to cancel the wholeprocess.
  private volatile boolean cancelled = false;
  // The exception that caused it to finish.
  private Exception thrown = null;

  @Override
  // The core run mechanism.
  public void run() {
    try {
      while (!cancelled) {
        // Block here if we're paused.
        blockIfPaused();
        // Do my work.
        step();
      }
    } catch (Exception ex) {
      // Just fall out when exception is thrown.
      thrown = ex;
    }
  }

  // Block if pause has been called without a matching resume.
  private void blockIfPaused() throws InterruptedException {
    try {
      // Grab a write lock. Will block if a read lock has been taken.
      pause.writeLock().lockInterruptibly();
    } finally {
      // Release the lock immediately to avoid blocking when pause is called.
      pause.writeLock().unlock();
    }

  }

  // Pause the work. NB: MUST be balanced by a resume.
  public void pause() {
    // We can wait for a lock here.
    pause.readLock().lock();
  }

  // Resume the work. NB: MUST be balanced by a pause.
  public void resume() {
    // Release the lock.
    pause.readLock().unlock();
  }

  // Stop.
  public void cancel() {
    // Stop everything.
    cancelled = true;
  }

  // start - like a thread.
  public void start() {
    // Wrap it in a thread.
    new Thread(this).start();
  }

  // Get the exceptuion that was thrown to stop the thread or null if the thread was cancelled.
  public Exception getThrown() {
    return thrown;
  }

  // Create this method to do stuff. 
  // Calls to this method will stop when pause is called.
  // Any thrown exception stops the whole process.
  public abstract void step() throws Exception;

  // Factory to wrap a Stepper in a PauseableThread
  public static PauseableThread make(Stepper stepper) {
    StepperThread pauseableStepper = new StepperThread(stepper);
    // That's the thread they can pause/resume.
    return pauseableStepper;
  }

  // One of these must be used.
  public interface Stepper {
    // A Stepper has a step method.
    // Any exception thrown causes the enclosing thread to stop.
    public void step() throws Exception;
  }

  // Holder for a Stepper.
  private static class StepperThread extends PauseableThread {
    private final Stepper stepper;

    StepperThread(Stepper stepper) {
      this.stepper = stepper;
    }

    @Override
    public void step() throws Exception {
      stepper.step();
    }
  }

  // My test counter.
  static int n = 0;

  // Test/demo.
  public static void main(String[] args) throws InterruptedException {

    try {
      // Simple stepper that just increments n.
      Stepper s = new Stepper() {
        @Override
        public void step() throws Exception {
          n += 1;
          Thread.sleep(10);
        }
      };
      PauseableThread t = PauseableThread.make(s);
      // Start it up.
      t.start();
      Thread.sleep(1000);
      t.pause();
      System.out.println("Paused: " + n);
      Thread.sleep(1000);
      System.out.println("Resuminng: " + n);
      t.resume();
      Thread.sleep(1000);
      t.cancel();
    } catch (Exception e) {
    }
  }
}

Edit: Code modified to be of more general use.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
1

Your best options are to either use wait()/notify() or to simply switch to ScheduledExecutorService

Proper wait()/notify() usage can be tricky. I highly recommend "Java Concurrency in Practice" to learn more about threading.

Eric Burke
  • 4,422
  • 3
  • 28
  • 29
0

I believe the best way here would be to use Thread.wait for the waiting thread instead of sleeping, and use Thread.notify in the thread you are waiting for. More info here: http://www.javamex.com/tutorials/synchronization_wait_notify.shtml

IncrediApp
  • 10,303
  • 2
  • 33
  • 24
0

You can improve efficiency drastic by using a monitor instead of sleeping the thread. You just make blocks in your code with a keyword synchronized. And an final Object that's acts the monitor. Look uP more in the API on monitors.

Joelmob
  • 1,076
  • 2
  • 10
  • 22