The Sun Certified Java Developer Exam with J2SE 5: paper version from Amazon, PDF from Apress, Online reference: Books 24x7 Personal blog
First, let me put a little meat into the codes:But if multiple threads can go into wait() state on a single mutex waiting for different conditions, then you really need to notify all of them so that they can all check whether their personal condition has changed.
So if you had a mutex on recordOne then you know that there is only one condition for this mutex (recordOne is either locked or unlocked) and you could safely call notify() on a single thread waiting for recordOne.
But if you had a mutex on a collection of records, then calling notify() on that collection will only transition one thread to blocked state (and then to runnable state), and when it transitions to runnable it may find that the condition it was waiting for (the individual record lock within the collection) is still not available - which will result in all the waiting threads returning to waiting state, even though there is potentially a lock free.
...JVM chooses T3. T3 runs line 4, go back to Waiting state, BUT also releases the lockedRecords monitor. No other threads are in the Blocked state.
Originally posted by Derek Canaan:
Hi Max,
First, thanks for your contributions in this forum. They helped me a lot. Please help me out here as i am trying to get a grip on this issue.
...excellent analysis snipped
Q8 is still grey for me, but i can see a little light at the end of the tunnel. Let me see if i understood your following statements correctly:
[Andrew]: But if multiple threads can go into wait() state on a single mutex waiting for different conditions, then you really need to notify all of them so that they can all check whether their personal condition has changed.
So if you had a mutex on recordOne then you know that there is only one condition for this mutex (recordOne is either locked or unlocked) and you could safely call notify() on a single thread waiting for recordOne.
But if you had a mutex on a collection of records, then calling notify() on that collection will only transition one thread to blocked state (and then to runnable state), and when it transitions to runnable it may find that the condition it was waiting for (the individual record lock within the collection) is still not available - which will result in all the waiting threads returning to waiting state, even though there is potentially a lock free.
First, let me put a little meat into the codes:
code:
Suppose
1. Thread T12 runs lock(recNo) and successfully put recNo 12 into lockedRecords and exits lock method but has not started on unlock(recNo 12).
2. Since T12 exited, lockedRecords monitor is available. Suppose T35 runs lock(recNo), acquires lockedRecords monitor and successfully put recNo 35 into lockedRecords and exits lock method but has not started on unlock(recNo 35).
3. Now T1 and T2 would like to lock(recNo 12) and T3 and T5 would like to lock (recNo 35).
4. Threads T1, T2, T3, T5 successively got lockedRecords monitor but go to Waiting state because recNo 12 and 35 are still contained in lockedRecords.
5. T12 manages to get lockedRecords monitor and run unlock(recNo12) and does a lockedRecords.notifyAll()
Q1. In this case, all of threads T1, 2, 3, 5 will go to Blocked state and JVM will select one to Running state. Suppose T3 got selected, T3 wastes some CPU, and go back to Waiting state, BUT at the same time releases lockedRecords monitor. So JVM selects one from the Blocked state (T1, 2, and 5) but NOT T3 as it is in Waiting state. Is this understanding correct?
Q2. Now let's suppose lockedRecords.notify() is used instead of notifyAll. So JVM chooses one thread from (T1,2,3,5) from Waiting state to Blocked state. Let's consider the worst scenario: JVM chooses T3. T3 runs line 4, go back to Waiting state, BUT also releases the lockedRecords monitor. No other threads are in the Blocked state -> Thread starvation.
Nobody calls notify, so these threads cannot come out of the Waiting state. T35 will eventually invoke lockedRecords.notify() but in effect one out of T1,2,3,5 will get to call unlock(). The other 3 threads starve. Is this correct?
Follow-up post...:
Hey, wait a minute ... there may be no starvation even for notify()
Let's continue the analysis:
...JVM chooses T3. T3 runs line 4, go back to Waiting state, BUT also releases the lockedRecords monitor. No other threads are in the Blocked state.
But thread T35 to the rescue. T35 eventually runs unlock(recNo 35) and does a lockedRecords.notify().
1. Because T35 calls lockedRecords.notify(), JVM chooses one of (T1,2,3,5) from the Waiting state to Blocked state. (Remember now that recNo 12, and 35 are no longer in lockedRecords.)
2. Say JVM chooses T1 (it does not matter).
3. T1 goes to Running state because T35 releases the lockedRecords monitor when it exits unlock(recNo), and there is only one thread namely T1 in the Blocked state.
4. At this stage, T2,3,5 are in Waiting state.
5. T1 runs lock(), put recNo 12 into lockedRecords and finally, unlock() removing recNo 12 from lockedRecords and calls lockedRecords.notify(). (Now, again, recNo 12, and 35 are not in lockedRecords)
6. Because T1 calls lockedRecords.notify(), JVM chooses one of (T2,3,5) from the Waiting state to Block state.
7. Say JVM chooses T2 (it does not matter).
8. T2 goes to Running state because T1 releases the lockedRecords monitor when it exits unlock(recNo), and there is only one thread namely T2 in the Blocked state.
9. At this stage, T3,5 are in Waiting state.
10. T2 runs lock(), put recNo 12 into lockedRecords and finally, unlock() removing recNo 12 from lockedRecords and call lockedRecords.notify(). (Now, again, recNo 12, and 35 are not in lockedRecords)
11. Because T2 calls lockedRecords.notify(), JVM chooses one of (T3,5) from the Waiting state to Block state and so on ..., and hence the remaining threads 3 and 5 will also go to Running state.
Hence no thread starvation!
Is this correct?
Q1. In this case, all of threads T1, 2, 3, 5 will go to Blocked state and JVM will select one to Running state. Suppose T3 got selected, T3 wastes some CPU, and go back to Waiting state, BUT at the same time releases lockedRecords monitor. So JVM selects one from the Blocked state (T1, 2, and 5) but NOT T3 as it is in Waiting state. Is this understanding correct?
Q2. Now let's suppose lockedRecords.notify() is used instead of notifyAll. So JVM chooses one thread from (T1,2,3,5) from Waiting state to Blocked state. Let's consider the worst scenario: JVM chooses T3. T3 runs line 4, go back to Waiting state, BUT also releases the lockedRecords monitor. No other threads are in the Blocked state -> Thread starvation.
Nobody calls notify, so these threads cannot come out of the Waiting state. T35 will eventually invoke lockedRecords.notify() but in effect one out of T1,2,3,5 will get to call unlock(). The other 3 threads starve. Is this correct?
Follow-up post (...) Is this correct?
No. The problem is that each time you write "(it does not matter)", in fact it *DOES*.
Andrew:
So if you had a mutex on recordOne then you know that there is only one condition for this mutex (recordOne is either locked or unlocked) and you could safely call notify() on a single thread waiting for recordOne.
But if you had a mutex on a collection of records, then calling notify() on that collection will only transition one thread to blocked state (and then to runnable state), and when it transitions to runnable it may find that the condition it was waiting for (the individual record lock within the collection) is still not available - which will result in all the waiting threads returning to waiting state, even though there is potentially a lock free.
Analysis below does show that T2 benefits from a future unlock:[Phil]:T1 owns L1
T2 waits for L1
T3 owns L2
T4 waits for L2
T1 unlocks and calls notify().
T4 is chosen and ... keeps waiting.
T3 unlocks and calls notify()
T4 is chosen again( why not) and is granted L2
T2 is still waiting... for ever? No, not sure, it may benefit of a future unlock, but it missed a chance to get L1 when it was released
[Andrew]:If you only have a single condition to check then you are quite safe with just using notify(). But if multiple threads can go into wait() state on a single mutex waiting for different conditions, then you really need to notify all of them so that they can all check whether their personal condition has changed.
So if you had a mutex on recordOne then you know that there is only one condition for this mutex (recordOne is either locked or unlocked) and you could safely call notify() on a single thread waiting for recordOne.
.Here's the thing: Most operating systems use a combination of PE and TS, exactly for this sort of issue(Thread(Max) waking up again and again). That is, all other things being equal, the priority of the thread that's been waiting the longest gets bumped a bit, eventually culminating into a winning advantage, and starvation is avoided
Admittedly, i did miss out the conjunction "but". My sentence should read, "Q2. Phil mentioned that notify() should be deprecated, but in this thread, Peter said notify() has its place. But at the moment, analysis shows notifyAll is safer and notify() starves threads even for a single condition check."[Javini]:By the way, are you sure Phil said that notify() should be deprecated (admittedly your sentence is ambiguous as to which person "in this thread" refers to);
[Phil]:Though, reading Max, it looks like notify() should be deprecated, yet it isn't... (anyway not yet)
LOL, you are right about the inquisitive part.[Javini]:Keep in mind that it may take a while to understand threads, thought certainly you seem to know quite a lot and are very inquisitive
Because notify() nullifies OS priorities setting for waiting threads, T1 continue to starve. If notifyAll() is used, T1 gets to run.
The Sun Certified Java Developer Exam with J2SE 5: paper version from Amazon, PDF from Apress, Online reference: Books 24x7 Personal blog
If you only have a single condition to check then you are quite safe with just using notify(). But if multiple threads can go into wait() state on a single mutex waiting for different conditions, then you really need to notify all of them so that they can all check whether their personal condition has changed.
Q1.If "[Phil]T2 missed a chance to get L1 when it was released[/Phil]", does this constitute thread starvation?
Q2. When notifyAll() is used instead, T2 can also miss a chance to get L1 when it was released.
Q3. Could we conclude from the analysis, that for designs synchronizing on a record collection monitor and calling notify(), there still will not be any thread starvation?
Q1. Can we conclude that we should never use notify() even if there is only one condition for the mutex?
Q2. Phil mentioned that notify() should be deprecated, in this thread, Peter said notify() has its place. But at the moment, analysis shows notifyAll is safer and notify() starves threads even for a single condition check.
Max:
notifyAll is more adaptable(in case you actually need to notify more then a single thread in the future), and it's just as efficient. That is, I would challenge you to even be able to measure the time between notifyAll and notify when a single thread is waiting.
Javini:
By the way, are you sure Phil said that notify() should be deprecated (admittedly
your sentence is ambiguous as to which person "in this thread" refers to); this
is rather alarming, if true, because in Phils specific notification algorithm, that is exactly where
you would want to use notify() instead of notifyAll() (as pointed out by Bloch, assuming
I understood Bloch correctly, as it is getting late, and it is time for bed).
Javini:
Keep in mind that it may take a while to understand threads, thought certainly you
seem to know quite a lot and are very inquisitive.