multithreading beginner question

As you can tell I'm new to multithreading and a bit stuck here. For my program I need a thread (PchangeThread in the below example) that can be toggled on and off from another thread at any point during execution of the program. The thread should be suspended on start and resume when pixelDetectorOn() is called.

The two threads will most likely not need to share any data except for a start/stop flag. I included a reference to the main thread anyway, just in case.

However, in the below code the only message that is ever output is "before entering loop", which indicates that the thread never wakes up from wait() for some reason. I'm guessing this is some kind of locking problem but I haven't been able to figure out what exactly is going wrong. Locking on this.detector from the main thread gives me the same result. Also I'm wondering if the wait()/notify() paradigm is really the way to go for suspending and waking the thread.

public class PchangeThread extends Thread {
  Automation _automation;
  private volatile boolean threadInterrupted;

  PchangeThread(Automation automation)
  {
    this._automation = automation;
    this.threadInterrupted = true;
  }

  @Override
  public void run()
  {
    while (true) {
      synchronized (this) {
        System.out.println("before entering loop");
        while (threadInterrupted == true) {
          try {
            wait();
            System.out.println("after wait");
          } catch (InterruptedException ex) {
            System.out.println("thread2: caught interrupt!");
          }
        }
      }
      process();
    }
  }

  private void process()
  {
    System.out.println("thread is running!");

  }

  public boolean isThreadInterrupted()
  {
    return threadInterrupted;
  }

  public synchronized void resumeThread()
  {
    this.threadInterrupted = false;
    notify();
  }
}

resumeThread() is called from the main thread the following way:

public synchronized void pixelDetectorOn(Context stateInformation) {        
        this.detector.resumeThread();
}

detector is a reference to an instance of PchangeThread. The "detector"-thread is instantiated in the program's main module the following way:

detector=new PchangeThread(this);

Answers


As you said, you need to protect access to the shared flag. You declared threadInterrupted volatile, but than are still using syncronized. You only need one. I prefer to just use syncronized as it makes things simpler. Multi-threading is complicated enough, keep it simple unless you know you need more complicated controls. This means that any time threadInterrupted is read or written to, the access should be synchronized. Currently, you are not doing that in setThreadInterrupt() and isThreadInterrupted().

Secondly, you want to synchronize on as small of a code block as possible. Inside of run(), you are synchronizing over the inner loop. In actuality, you only need to to synchronize on the read of threadInterrupted. When the implementation of isThreadInterrupted() is fixed as mentioned above, you can use that directly and remove the synchronized block from the inner loop.

The fact that you are synchronizing on the inner loop, is the error that is causing your code to never print "thread is running!". PchangeThread acquires the lock on itself and calls wait() to suspend the thread. However, the thread is still holding the lock at this point. At some point later, the main thread calls resumeThread() in order to restart the thread. However, that method can not begin its execution because it must first wait to acquire the lock. However, it will never get the lock until the PchangeThread is notified.

You are providing two ways to set threadInterrupted, but only one of them notifies the thread when the value is set to false. Do you really need setThreadInterrupt()? I expect you don't. If you keep it, it should act the same as resumeThread() when the argument is false.

Lastly, it is better to lock on a private object instead of the instance itself. You have complete control over the private lock object. However, anyone with a reference to your thread instance could also use it as the lock for a synchronized block, which could potentially lead to a hard to find deadlock.

Your code altered to use my edits:

public class PchangeThread extends Thread {
  private final Object _lock = new Object();
  Automation _automation;
  private final boolean _threadInterrupted;

  PchangeThread(Automation automation)
  {
    _automation = automation;
    _threadInterrupted = true;
  }

  @Override
  public void run()
  {
    while (true) {
      System.out.println("before entering loop");
      while (isThreadInterrupted()) {
        try {
          wait();
          System.out.println("after wait");
        } catch (InterruptedException ex) {
          System.out.println("thread2: caught interrupt!");
        }
      }
      process();
    }
  }

  private void process()
  {
    System.out.println("thread is running!");

  }

  public boolean isThreadInterrupted()
  {
    synchronized (_lock) {
      return _threadInterrupted;
    }
  }

  public void resumeThread()
  {
    synchronized (_lock) {
      _threadInterrupted = false;
      notify();
    }
  }
}

Need Your Help

AudioBufferList contents in remoteIO audio unit playback callback

ios audio audiounit

I'd like to "intercept" audio data on its way to the iOS device's speaker. I believe this can be done using remoteIO audio units and callbacks. In the playbackCallback below does ioData actually co...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.