Versions Compared

Key

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

...

The notify() and notifyAll() methods of package java.lang.Object are used to wake up a waiting thread (s)or threads, respectively. These methods must be invoked from a thread that holds the same object lock as the waiting thread(s); these methods throw an IllegalMonitorStateException when invoked from any other thread. The notifyAll() method wakes up all threads waiting on an object lock and allows threads whose condition predicate is true to resume execution. Furthermore, if all the threads whose condition predicate evaluates to true previously held a specific lock before going into the wait state, only one of them will reacquire the lock upon being notified. Presumably, the other threads will resume waiting. The notify() method wakes up only one thread, with no guarantee regarding which specific thread is notified. The chosen thread is permitted to resume waiting if its condition predicate is unsatisfied, and ; this often defeats the purpose of the notification.

...

The java.util.concurrent.locks utilities provide the Condition.signal() and Condition.signalAll() methods to awaken threads that are blocked on a Condition.await() call. Condition objects are required when using java.util.concurrent.locks.Lock objects. A Although Lock object allows objects allow the use of Object.wait(), Object.notify(), and Object.notifyAll() methods, however this is prohibited by rule "LCK03-J. Do not synchronize on the intrinsic locks of high-level concurrency objects". Code that synchronizes using a Lock object uses one or more Condition objects associated with the Lock object rather than using its own intrinsic lock. These objects interact directly with the locking policy enforced by the Lock object. Consequently, the await(), signal(), and signalAll() methods are used in place of the wait(), notify(), and notifyAll() methods.

...

This noncompliant code example shows a complex multi-step , multistep process being undertaken by several threads. Each thread executes the step identified by the time field. Each thread waits for the time field to indicate that it is time to perform the corresponding thread's step. After performing the step, each thread first increments time and then notifies the thread that is responsible for the next step.

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 Perform operations when field time 
                          // reaches this value

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

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

        // Perform 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();
    }
  }
}

...

In this compliant solution, each thread completes its step and then calls notifyAll() to notify the waiting threads. The thread that is ready can then perform its task , while all the threads whose condition predicates are false (loop condition expression is true) promptly resume waiting.

...

Code Block
bgColor#ccccff
public final class ProcessStep implements Runnable {
  private static final Object lock = new Object();
  private static int time = 0;
  private final int step; // DoPerform operations when field time reaches
  this                         // reaches this value
  public ProcessStep(int step) {
    this.step = step;
  }

  @Override public void run() {
    try {
      synchronized (lock) {
        while (time != step) {
          lock.wait();
        }
  
        // Perform operations
  
        time++;
        lock.notifyAll(); // Use notifyAll() instead of notify()
      }
    } catch (InterruptedException ie) {
      Thread.currentThread().interrupt(); // Reset interrupted status
    }
  }

}

...

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; // DoPerform operations when field time reaches
 this value

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

  @Override public void run()       // reaches this value
  public ProcessStep(int step) {
    this.step = step;
  }

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

      // Perform 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();
    }
  }
}

...

Wiki Markup
This compliant solution uses the {{signalAll()}} method to notify all waiting threads. Before {{await()}} returns, the current thread reacquires the lock associated with this condition. When the thread returns, it is guaranteed to hold this lock \[[API 2006|AA. Bibliography#API 06]\]. The thread that is ready can perform its task, while all the threads whose condition predicates are false resume waiting.

...

Code Block
bgColor#ccccff
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; // DoPerform operations when field time 
                          // reaches this value
  public ProcessStep(int step) {
    this.step = step;
  }

    @Override public void run() {
      lock.lock();
      try {
        while (time != step) {
          condition.await();
        }
  
        // Perform operations

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

}

Compliant Solution (Unique Condition

...

per Thread)

This compliant solution assigns each thread its own condition. All the Condition objects are accessible to all the threads.

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 field time;
  private final int step; // Perform operations when field time 
                          // 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");
    }
  }

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

      // Perform 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 ps = new ProcessStep(i);
      new Thread(ps).start();
    }
  }
}

...

Notifying a single thread rather than all waiting threads can pose a threat to violate the liveness property of the system.

...

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="105e0be58cb1ed6f-c3703eaf-4a814e97-b666b5c3-118c8ee5215041258d8e811b"><ac:plain-text-body><![CDATA[

[[API 2006

AA. Bibliography#API 06]]

java.util.concurrent.locks.Condition interface

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="ecf632c49ebc0469-b2032f05-4f9b4ce1-a9bdb1de-8ce4a00c56d1e27e743aabf2"><ac:plain-text-body><![CDATA[

[[JLS 2005

AA. Bibliography#JLS 05]]

[Chapter 17, Threads and Locks

http://java.sun.com/docs/books/jls/third_edition/html/memory.html]

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="cb826fbccb2bf979-cf3d07a2-4bf94df5-b78a8c76-4d3b2fa372aff93c7b9dfa56"><ac:plain-text-body><![CDATA[

[[Goetz 2006

AA. Bibliography#Goetz 06]]

Section 14.2.4, Notification

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="c2433ae94fae8e39-74473670-4271458e-b4c9a75c-d86acf5936c0ad59db213d6e"><ac:plain-text-body><![CDATA[

[[Bloch 2001

AA. Bibliography#Bloch 01]]

Item 50: . Never invoke wait outside a loop

]]></ac:plain-text-body></ac:structured-macro>

...