...
This noncompliant code example demonstrates 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 SleepyThreadProcessStep implements Runnable { private static final Object lock = new Object(); private static int time = 1; private int myStep; // do stuff when the step raches this value public SleepyThreadProcessStep(int myStep) { this.myStep = myStep; } public void run () { try { synchronized (lock) { while (time != myStep) { lock.wait(); } // ... do stuff time++; lock.notify(); } } catch (InterruptedException ie) { Thread.currentThread().interrupt(); // Reset interrupted status } } public static void main(String[] args) { for (int i = 5; i > 0; i--) { SleepyThreadProcessStep ms = new SleepyThreadProcessStep(i); new Thread(ms).start(); } } } |
...
Code Block | ||
---|---|---|
| ||
public void run () {
try {
synchronized (lock) {
while (time != myStep) {
lock.wait();
}
// ... do stuff
time++;
lock.notifyAll();
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // Reset interrupted status
}
}
|
...
This noncompliant code example derives from the previous noncompliant code example but uses the Condition
interface. Field cond
condition
is used to let threads wait on different condition predicates.
Code Block | ||
---|---|---|
| ||
public class ProcessStep implements Runnable { private static final ConditionLock condlock = new lock.newConditionReentrantLock(); // ... public void run (){ private static final Condition condition = lock.locknewCondition(); private static tryint {time = 1; private int myStep; // do stuff if(number == 1) { while(buffer_count == 0) { when the step raches this value public ProcessStep(int myStep) { this.myStep cond.await(); = myStep; } public void } else if(number == 2run() { lock.lock(); try { while (buffer_counttime =!= 10myStep) { condcondition.await(); } // ... do stuff time++; }condition.signal(); } catch (InterruptedException ie) { } else if(number == 3) Thread.currentThread().interrupt(); // Reset interrupted status } finally { condlock.signalunlock(); } } public static }void catch (InterruptedException iemain(String[] args) { // Handle the exception Thread.currentThread().interrupt(); // Reset interrupted status } finally { lock.unlock(); } } for (int i = 5; i > 0; i--) { ProcessStep ms = new ProcessStep(i); new Thread(ms).start(); } } } |
Similar to Object.notify()
, the cond.signal()
method may choose either of the threads any one thread and awaken it.
Compliant Solution (signalAll()
)
This compliant solution uses the signalAll()
method to resume all the waiting threads whose condition predicate allows doing so.
Code Block | ||
---|---|---|
| ||
} else if(number == 3) { cond public void run() { lock.lock(); try { while (time != myStep) { condition.await(); } // ... do stuff time++; condition.signalAll(); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); // Reset interrupted status } finally { lock.unlock(); } } |
Compliant Solution (unique Condition
...
per thread)
This compliant solution uses two different condition variables full
and empty
to indicate whether the the buffer is full or empty, respectivelyassigns each thread its own Condition
, and makes them accessible to all the threads.
Code Block | ||
---|---|---|
| ||
public class ProcessStep implements Runnable { private static final Lock lock = new ReentrantLock(); private static final Condition[] fullconditions = lock.newCondition(); final Condition empty = lock.newCondition(); // ... public void run (){ lock.lock(); try { if(number == 1) { new Condition[6]; private static int time = 1; private int myStep; // do stuff when the step raches this value public ProcessStep(int myStep) { this.myStep = myStep; conditions[myStep] = lock.newCondition(); } public void run() { lock.lock(); try { while (buffer_counttime !== 0myStep) { emptyconditions[myStep].await(); } } else if(number == 2) { while(buffer_count == 10) { // ... do stuff time++; if (myStep + 1 < conditions.length) { full.awaitconditions[myStep+1].signal(); } } catch (InterruptedException ie) { } Thread.currentThread().interrupt(); // Reset interrupted status } else if(number == 3) finally { emptylock.signalunlock(); } full.signal();} public static void main(String[] args) { }for (int i = 5; i }> catch (InterruptedException ie0; i--) { // Handle theProcessStep exception ms = new Thread.currentThreadProcessStep(i).interrupt(); // Reset interrupted status } finally { lock.unlocknew Thread(ms).start(); } } } } |
Even though signal()
is used, it is guaranteed that only one thread will awaken because each condition predicate corresponds to a unique Condition
variable.
...