Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • Every condition predicate in every waiting thread would be true (condition expressions in loops will be false) if a notification were received by each, independently. Furthermore, all these threads must perform the same set of operations after waking up. In other words, any one thread can be selected to wake up and resume for a single invocation of notify().
  • Only one thread is required to wake up on the notify signal. This is contingent on the condition predicate, in that, only one predicate must fulfill the condition and allow the thread to proceed. Multiple condition predicates in the same statement should be avoided.
  • No untrusted code has access to the object being waited on. If untrusted code has access to this object, it can invoke wait() on the object and intercept a notify() call.

...

Code Block
bgColor#FFcccc
public final class ProcessStep implements Runnable {
  private static final Object lock = new Object();
  private static int time = 0;
  private final int step; // Do operations when thefield steptime reaches this value

  public ProcessStep(int step) {
    this.step = step;
  }

  public void run() {
    try {
      synchronized (lock) {
        while (time != step) { 
          lock.wait();  
        }

        // ... Do operations

        time++;
        lock.notify();
      }
    } catch (InterruptedException ie) {
      Thread.currentThread().interrupt(); // Reset interrupted status
    }    
  }

  public static void main(String[] args) {
    for (int i = 4; i >= 0; i--) {
      new Thread(new ProcessStep(i)).start();
    }
  }
}

This noncompliant code example violates the liveness property. Each thread has a different condition predicate, as each requires step to have a different value before proceeding. The Object.notify() method wakes up only one thread at a time. Unless it happens to wake up the thread that is required to perform the next step, the program stalls and fails to make any progress.

Compliant Solution (notifyAll())

...

This noncompliant code example derives from the previous noncompliant code example but uses the Condition interface. Field The condition field allows the threads to wait on different condition predicates.

Code Block
bgColor#FFcccc
public class ProcessStep implements Runnable {
  private static final Lock lock = new ReentrantLock();
  private static final Condition condition = lock.newCondition();
  private static int time = 0;
  private final int step; // Do operations when thefield steptime reaches this value

  public ProcessStep(int step) {
    this.step = step;
  }

  public void run() {
    lock.lock();
    try {
      while (time != step) { 
        condition.await();  
      }

      // ... Do operations

      time++;
      condition.signal();
    } catch (InterruptedException ie) {
      Thread.currentThread().interrupt(); // Reset interrupted status
    } finally {
      lock.unlock();
    }
  }

  public static void main(String[] args) {
    for (int i = 4; i >= 0; i--) {
      new Thread(new ProcessStep(i)).start();
    }
  }
}

...

Code Block
bgColor#ccccff
// Declare class as final because its constructor throws an exception 
public final class ProcessStep implements Runnable { 
  private static final Lock lock = new ReentrantLock();
  private static int time = 0;
  private final int step; // Do operations when thefield steptime reaches this value
  private static final int MAX_STEPS = 5;
  private static final Condition[] conditions = new Condition[MAX_STEPS];

  public ProcessStep(int step) {
    if (step <= MAX_STEPS) {
      this.step = step;
      conditions[step] = lock.newCondition();
    } else {
      throw new IllegalArgumentException("Too many threads");
    }
  }

  public void run() {
    lock.lock();
    try {
      while (time != step) { 
        conditions[step].await();  
      }

      // ... Do operations

      time++;
      if (step + 1 < conditions.length) {
        conditions[step + 1].signal();
      }
    } catch (InterruptedException ie) {
      Thread.currentThread().interrupt(); // Reset interrupted status
    } finally {
      lock.unlock();
    }
  }

  public static void main(String[] args) {
    for (int i = MAX_STEPS - 1; i >= 0; i--) {
      ProcessStep ms = new ProcessStep(i);
      new Thread(ms).start();
    }
  }
}

...