The methods {{notify()}} and {{notifyAll()}} are used to waken waiting thread(s). These methods must be called from code that holds the same object lock as the waiting thread(s). The method {{notify()}} is deceptive in most cases unless all of the following conditions hold: \[[Goetz 06|AA. Java References#Goetz 06]\] |
These requirements are typically true when only one thread is waiting. Otherwise, if either condition does not hold, incorrect program behavior may result.
This noncompliant code example violates the liveness property. A lock is held on a shared object list
and three threads are started. Two condition predicates are used. One checks whether the buffer has zero elements and the other checks if the buffer is full with ten elements. Initially the buffer is neither full nor empty. Conditions are created so that the buffer becomes empty and thread 1 goes into wait state, followed by thread 2.
Note that when thread 2 goes into the wait state, the condition predicate of thread 1 becomes false
. When notify()
is invoked by thread 3, it can be delivered to either thread 1 or thread 2 depending on the particular Java Virtual Machine (JVM). If thread 1 is chosen, its condition turns out to be false
. Even though this notification is meant for thread 2 (whose condition predicate is on the other hand, true
) thread 1 wakes up when its condition predicate is still false
.
public class MissedSignal implements Runnable { private static LinkedList list = new LinkedList(); private static int buffer_count = 5; private int number; // Selects function based on thread number public MissedSignal(int number) { this.number = number; } public void run (){ synchronized(list) { try { if(number == 1) { System.out.println("Thread 1 started..."); while(buffer_count == 0) { System.out.println("Beginning wait() Thread 1..."); list.wait(); System.out.println("Thread 1 got notified this time..."); } System.out.println("Exiting because Thread 1 condition is false..."); } else if(number == 2) { System.out.println("Thread 2..."); while(buffer_count > 0) { System.out.println("Beginning wait() Thread 2..."); list.wait(); System.out.println("Thread 2 got notified this time..."); } System.out.println("Exiting because the thread 2 condition is false..."); } else if(number == 3) { Thread.sleep(2000); list.notify(); } }catch (InterruptedException ie) { // Handle the exception } } } public static void makeThread1True() { buffer_count = 0; } public static void makeThread2True() { buffer_count = 10; } public static void main(String[] args) throws IOException { makeThread1True(); Runnable runnable1 = new MissedSignal(1); Thread t1 = new Thread(runnable1); t1.start(); try { new Thread().sleep(5000); makeThread2True(); } catch (InterruptedException e) { // Handle the exception } Runnable runnable2 = new MissedSignal(2); Thread t2 = new Thread(runnable2); t2.start(); Runnable runnable3 = new MissedSignal(3); Thread t3 = new Thread(runnable3); t3.start(); } } |
This compliant solution uses the notifyAll()
method which sends notifications to all threads that wait on the same object. As a result, liveness is not affected unlike the noncompliant code example. The condition predicate controls which threads can resume their operations. Ensure that the lock is released promptly after the call to notifyAll()
. This is not a requirement in this particular example because it is a producer-consumer process.
else if(number == 3) { Thread.sleep(2000); list.notifyAll(); } |
EX1: If there are several similar threads waiting for a notification, and it is permissible to invoke any of them, notify()
may be used. The criteria for liveness is relaxed in this case.
Invoking the notify()
method instead of notifyAll()
can be a threat to the liveness property of the system.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
CON32- J |
low |
unlikely |
medium |
P2 |
L3 |
TODO
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
\[[JLS 05|AA. Java References#JLS 05]\] [Chapter 17, Threads and Locks|http://java.sun.com/docs/books/jls/third_edition/html/memory.html] \[[Goetz 06|AA. Java References#Goetz 06]\] Section 14.2.4, Notification \[[Bloch 01|AA. Java References#Bloch 01]\] Item 50: Never invoke wait outside a loop |
CON18-J. Always invoke wait() and await() methods inside a loop 11. Concurrency (CON) CON33-J. Address the shortcomings of the Singleton design pattern