...
This noncompliant code example violates the liveness property. A lock is obtained 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, when the buffer becomes fulldemonstrates a complex multi-step process being undertaken by several threads. Each thread executes one step of the process; the step being currently performed is maintained by the step
field. Each thread waits for the step
field to indicate that it is time to perform that thread's step. When it is time to perform its step, each thread does so. It then increments step
for the next thread, notifies the thread, and exits.
Code Block | ||
---|---|---|
| ||
public class MissedSignalSleepyThread implements Runnable { private static final Object lock = new Object(); private static int buffer_countstep = 51; private static int numbermyStep; // Selectsdo functionstuff basedwhen onthe threadstep number raches this value public void run (){ synchronized(lockSleepyThread(int myStep) { this.myStep = trymyStep; { } public void run if(number == 1) { while(buffer_count == 0)try { synchronized lock.wait(); (lock) { } } else if(number == 2while (time != myStep) { while(buffer_count == 10) { lock.wait(); } // ... do }stuff } else if(number == 3) { time++; lock.notify(); } } catch (InterruptedException ie) { // Handle the exception Thread.currentThread().interrupt(); // Reset interrupted status } } } public static void setThreadParametersmain(int threadNumber, int bufferCountString[] args) { for (int numberi = threadNumber5; i > buffer_count = bufferCount;0; i--) { } SleepyThread ms = new SleepyThread(i); new Thread(ms).start(); public static} void main(String[] args) throws IOException, InterruptedException { } } |
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 to perform the next step, the program will deadlock.
Compliant Solution (notifyAll())
In this compliant solution, each thread uses notifyAll()
to wake all of the other threads when its step is complete. Consequently the proper thread can perform its next step, while all other threads note that their condition predicate has not been satisified and promptly go back to sleep.
Code Block | ||
---|---|---|
| ||
public void run () { try { MissedSignal ms = newsynchronized MissedSignal(lock); { while for(inttime i != 0; i < 3; i++myStep) { // Buffer count does not matter for third thread setThreadParameters(i + 1, i * 10); // Sets buffer count for threads in the sequence: 0, 10, 20 lock.wait(); } // ... do stuff time++; new Thread(ms).startlock.notifyAll(); Thread.sleep(1000);} } } } |
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
, and consequently, it goes into the wait state once again. Thread 1 continues to wait despite its condition predicate being false
and is not terminated in this case.
Compliant Solution (notify all threads)
This compliant solution uses the notifyAll()
method which sends notifications to all threads that wait on the same lock 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()
.
Code Block | ||
---|---|---|
| ||
else if(number == 3) { list.notifyAll(); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); // Reset interrupted status } } |
Noncompliant Code Example (Condition
interface)
...