...
This noncompliant code example violates the liveness property. A lock is held on a shared object list
using a raw Object
lock
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 (buffer is not shown for brevity, only the count of the number of elements in the buffer at any time is shown). 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
., when the buffer becomes full.
Code Block | ||
---|---|---|
| ||
public class MissedSignal implements Runnable { private static LinkedListObject listlock = new LinkedListObject(); private static int buffer_count = 5; private static int number; // Selects function based on thread number public void MissedSignalsetThreadNumber(int numbernum) { this.number = numbernum; } public void run (){ synchronized(listlock) { try { if(number == 1) { System.out.println("Thread 1 started..."); while(buffer_count == 0) { System.out.println("Beginning wait() Thread 1..."); listlock.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 started..."); while(buffer_count >== 010) { System.out.println("Beginning wait() Thread 2..."); listlock.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); listlock.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, InterruptedException { makeThread1True(); RunnableMissedSignal runnable1ms = new MissedSignal(1); Thread t1 = new Threadms.makeThread1True(runnable1); t1ms.startsetThreadNumber(1); try { new Thread(ms).sleepstart(5000); makeThread2True(); } catch (InterruptedException e) {Thread.sleep(1000); // Handle the exception }ms.makeThread2True(); Runnable runnable2 = new MissedSignal ms.setThreadNumber(2); Thread t2 = new Thread(ms).start(runnable2); t2.start( Thread.sleep(1000); Runnable runnable3 = new MissedSignal ms.setThreadNumber(3); Thread t3 = new Thread(ms).start(runnable3); t3.start(); } } |
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 to be notified, its condition turns out to be false
which terminates it. This is the required functionality, that is, any thread whose condition predicate is false must be terminated. However, if the notification is delivered to thread 2, it has no effect because its condition predicate is still true. Consequently, thread 1 continues to wait despite its condition predicate being false and is not terminated in this case.
Compliant Solution
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.
Code Block | ||
---|---|---|
| ||
else if(number == 3) {
Thread.sleep(2000);
list.notifyAll();
}
|
Exceptions
...