Roman
Roman

Reputation: 4513

lock a Runnable until finished

I'm using Runnable as a loop in Android. As this:

    Timer timer = new Timer();
    timer.schedule(new looper(m_mainView, this),0, Rate);

Which runs every 'Rate' milliseconds.

The looper is as this:

class looper extends TimerTask
{
    private ImageView img;
    Context c;


    public looper(ImageView imgView, Context context)
    {
        this.img = imgView;
        this.c = context;
    }

    public void run()
    {   
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
....

I would like to lock the code inside run() until it is finished, so that if it is called before finished - the thread that calls will return and finish.

I've tried a synchronized(Object) approach inside run() which didn't work. Also tried a Mutex, which also didn't work.

Help :)

Upvotes: 0

Views: 2109

Answers (3)

Giovanni Botta
Giovanni Botta

Reputation: 9816

First of all, why is there any risk that the run method will be called again? If that's true it sounds like a design deficiency to me. Second, instead of TimerTask, you might be better off using a ScheduledExecutorService instead. That's the standard, simplest and safest way of executing scheduled tasks at a fixed interval. Your user code won't be accessible from anything but the executor service and you can get a ScheduledFuture from it to get a return value so that you know when the task is done (the ScheduledFuture blocks when you call get()). Take a look at Java Concurrency in Practice for more...

Upvotes: 0

OldCurmudgeon
OldCurmudgeon

Reputation: 65811

The looper object is solely owned by the timer.schedule so no-one can call that object's run method except the timer.schedule. If the run method takes longer that the period you have specified then it is likely that the run method will be called again before it completes - especially since you have passed off the actual running of the the task to the UI thread.

You have two simple alternatives:

  1. Set a flag in your run method indicating that you are running and make run do nothing if it is set.
  2. Fire off the timer after a fixed delay only, not with a repeat time. At the end of your run, fire it off again.

For 1:

class Looper extends TimerTask {
  // ** Add this **
  volatile boolean running = false;

  public Looper() {
  }

  @Override
  public void run() {
    // ** Add this **
    if (!running) {
      running = true;
      try {
        runOnUiThread(new Runnable() {
          @Override
          public void run() {
          }

        });
        // ** Add this **
      } finally {
        running = false;
      }

    }
  }

}

Second method:

timer.schedule(new looper(m_mainView, this, Rate),new Date());
...
class Looper extends TimerTask {
  final long rate;
  final Looper looper;

  public Looper(long rate) {
    this.rate = rate;
    looper = this;
    // ...
  }

  @Override
  public void run() {
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        // ...
        new Timer().schedule(looper, new Date(new Date().getTime() + rate));
      }

    });

  }

}

Upvotes: 1

EyalBellisha
EyalBellisha

Reputation: 1914

You should add synchronized to the method declaration like this:

public synchronized void run()

Upvotes: 0

Related Questions