Max:
I'd just to know if the people who lost points are people also didn't call notifyAll in lock.
I called notifyAll() in all of my object locking situations and still got counted off.
Trym:
Regarding the discussion about the locking 44/80 points:
I have focused on the requirement "while waiting using no CPU cycles". This I have accomplished calling notify instead of notifyAll.
People usually use notify() and ignore notifyAll() but I have a book it says notifyAll() is better than notify() and it mentions a phrase "miss notification", what does it mean? Is it ture? In the book, it says if there is only one thread waiting, notify() is OK, notifyAll() is OK but it uses more resource than notify(), that's a waste, but if there are more than one thread.
You have three possible cases :
1. You know that only one thread is waiting to be notified on a given object. In this case, notify() is better because more efficient.
2. You know that multiple threads may be waiting to be notified on a given object, but all threads are interchangeable : you need one of them to perform a job, but it can be *any* of them. In this case, notify() is still better, for the same reasons.
3. Multiple threads may be waiting to be notified on a given object, but they are not interchangeable for two possible reasons : they have a different interest in the notification (that's what happens with locking - each thread may be waiting on a different record lock (I thought there to solution 1.x of course ) or you need to awake all waiting threads for any reason of yours. In that case, notifyAll() *must* be used.
I have decided to use #2/4 ...
I will synchronize everything on one object this or mutex: All public methods (except getMetaData) in Data class. No more synchronized word in any other classes.
Locking
======
The implementation of the Services book method in ServicesImpl is the only user of the locking API. I specified a contract in the Javadoc that calls for any potential users of the locking API to invoke lock/process/unlock as a sequence within the context of a single method call which guarantees it will happen within a single thread of execution. When lock is invoked, a check is made to see if the Contractor already has been locked by another thread. If it has, the current thread will wait until notified that it has been unlocked. If not, a Lock object is created that has a reference to the current thread and it is put in the locking Map with the Contractor as the key. The protected processing operation is performed and then the Lock is removed from the Map and destoyed by the unlock method call. It is vital that any exception that occurs during processing is caught and rethrown after the Contractor has been unlocked. Deadlock is prevented by specifying a locking order to be used by programmers.
The call to notifyAll() in lock() is even worst because it's obvious that no waiting thread may be interested by a lock just granted. They wake up, check their waiting condition for nothing and return to sleep. With that design, each time a record is locked, all waiting threads consume (a lot of) CPU cycles uselessly, which breaks the requirements stated by the instructions.
quote: people usually use notify() and ignore notifyAll() but I have a book it says notifyAll() is better than notify() and it mentions a phrase "miss notification", what does it mean? Is it ture? In the book, it says if there is only one thread waiting, notify() is OK, notifyAll() is OK but it uses more resource than notify(), that's a waste, but if there are more than one thread.
Unfortunately, the book mentioned is not referenced.
kktec<br />SCJP, SCWCD, SCJD<br />"What we observe is not nature itself, but nature exposed to our method of questioning." - Werner Heisenberg
SCJP,SCJD,SCWCD,SCBCD,SCDJWS,SCEA
Suds, it's possible you're confusing acquiring and releasing a sync lock with lock()ing and unlock()ing a record. If one thread is trying to lock() a record which is already lock()ed by another thread, it's got to wait(). During this time, it really doesn't matter if other threads manage to lock() that record, or any other records. We only care if another thread manages to unlock() a record, because that might be the record we're interested in.
In Max's code, notifyAll() after a lock() makes sense only if there are two different types of lock() (DB-level and record-level) and one type can override another. E.g. if thread A is waiting for a record-level lock() and thread B succeeds in getting a DB-level lock(), thread A may need to be notified to stop waiting for one record, since the whole DB is now locked. Or not, depending on your design. But in general, there's no use for notifyAll() inside lock(). The other threads are really just interested in knowing if unlock() as been accomplished, not lock().
My guess on this 44/80 score is the documentation of design, explicitly explaining why design is thread-safe and no deadlock is possible. Also the need to state explicitly the contract that client need to adhere for thread safety. It would help our 'investigation' if the candidates who got 44/80 can confirm if they have justified their design as being free of deadlock in their documentation.
Another guess: 'defensive copying' - remember this?
While I think this argument about "breaks the requirements" may have some validity in reference to a category 2 design, I do not think it applies to category 1. In that situation, a thread waits on the locks container because it can't yet know whether the ultimate prize is indeed locked at that point in time. Once it gets the monitor on the locks container and it finds the resource locked, it waits and there is not a violation. The requirements state: "Any attempt to lock a resource that is already locked should cause the current thread to give up the CPU, consuming no CPU cycles until the desired resource becomes available." When the thread is notified, it starts all over again in its attempt to lock the resource so there is no violation, although there are some, not necessarily a lot of, wasted CPU cycles.
By and large, I agree with Ken here. Also, my book very clearly recommends notifyAll over notify, as do most other detailed SCJD books, including RHE.
Concerning the related topic you raised,
it seems you have defined two, basic types of locking mechanisms:
1. those that use notifyAll() in the unlock method(),
2. and those that use notify() in the unlock method()
Have you asked, or do you know which type of locking mechanism
Ken used?
Originally posted by Philippe Maquet:
Hi Max,
Including the notifyAll() in lock()? Did you change your mind since October about it?
I think it's really important to be precise about that. Hence I asked Ken above how he justified (I mean technically) his notifyAll() call in lock().
A good score obtained with a given technique - especially if it's isolated as in this case (remember the two bad scores mentioned in my first thread) - cannot be an insurance for others : human grading being an imperfect process by nature, it's still possible to make a mistake and be lucky enough for not loosing any point for it .
Best,
Phil.
Ken, I have two basic questions for you:
1. How did you justify the notifyAll() in lock()?
2. In which area do you think that solution 2 above "break the requirements"? Which one, and to what extent?
kktec<br />SCJP, SCWCD, SCJD<br />"What we observe is not nature itself, but nature exposed to our method of questioning." - Werner Heisenberg
In all honesty, I think you and I disagree just how relevant an issue this is.
Phil,
---
Ken, I have two basic questions for you:
1. How did you justify the notifyAll() in lock()?
2. In which area do you think that solution 2 above "break the requirements"? Which one, and to what extent?
---
1. Sorry, but I'm fresh out of time for today and will have to wait until tomorrow to give you my answer. I hope you will find it interesting.
2. I'm not saying that a type 2 solution breaks the requirements. I'm merely saying that a type 2 that uses notifyAll() may do so if the very strict interpretation of the requirements that you made is used by the grader. It is hard to be certain what interpretation a grader may make of this.
Originally posted by Philippe Maquet:
Hi Max,
I think we should agree on an issue which *is* relevant for sure: why so many people fail in locking with a score of 44/80.
As I wrote in my first post, we explored a few tracks in trying to explain it.
The notification technique used is only one of them, and you started it yourself on the results forum by writing:
I'd just to know if the people who lost points are people also didn't call notifyAll in lock.
It let me think that the way people notify waiting threads for locks *could* be relevant to explain why people fail in locking, but it looks like it's a bit earlier to conclude yet.
All design patterns describe a context, a problem and a solution, all three being understandable. No pattern is "self-justified", by general terms like "good practice" or "commonly accepted technique". I cannot understand such "arguments", but logical demonstrations, well-documented design patterns, language specs, etc. And as I generally don't believe in anything I cannot understand...
Regards,
Phil.
[Phil]:But I *know* that he didn't call notifyAll() in lock() because he was the first guy here to point out the issue in Max's locking example.
I think you still get the 'win' here.
The post that Max conceded on notifyAll in lock() is found in this thread and i quote Max:
Defensive copying is brought out by Vlad and Jim, in which the Data class'
readRecord method returns to the client a copy of the record instead of the
actual record reference itself. The danger of returning the actual reference is the client may do something with it that changes the states of the record reference without the proper locking protection. For example:
code:
record = db.readRecord(5);
record.setName("Sun");
This effectively also changes the name field of record 5 in the database cache.
In this scenario, the system's locking mechanism is breached.
Originally posted by Derek Canaan:
Hi Phil, This effectively also changes the name field of record 5 in the database cache.
In this scenario, the system's locking mechanism is breached.
rgds,
derek
[ February 10, 2004: Message edited by: Derek Canaan ]
Originally posted by Philippe Maquet:
Hi Derek,
Good catch! And it was worth while to recall it. Thanks again.
Regards,
Phil.
Originally posted by Philippe Maquet:
Hi Max,
The issue pointed by Derek has nothing to do with locking but with cache write access as a side effect.
Ken, I have two basic questions for you:
1. How did you justify the notifyAll() in lock()?
2. In which area do you think that solution 2 above "break the requirements"? Which one, and to what extent?
---
1. Sorry, but I'm fresh out of time for today and will have to wait until tomorrow to give you my answer. I hope you will find it interesting.
2. I'm not saying that a type 2 solution breaks the requirements. I'm merely saying that a type 2 that uses notifyAll() may do so if the very strict interpretation of the requirements that you made is used by the grader. It is hard to be certain what interpretation a grader may make of this.
1. For sure!
2. notifyAll() in solution 2, where - by design - you *know* that one-and-only-one thread is waiting on the notified monitor?! Of course it won't hurt anybody, but it looks like telling someone: "I'll notify *all* passengers of that skateboard"! I guess I missed something in your reasoning. Could you explain it too?
Best,
Phil.
The call to notifyAll() in lock() is even worst because it's obvious that no waiting thread may be interested by a lock just granted. They wake up, check their waiting condition for nothing and return to sleep. With that design, each time a record is locked, all waiting threads consume (a lot of) CPU cycles uselessly, which breaks the requirements stated by the instructions.
...I do not think it ( i.e. "breaks the requirments") applies to category 1. In that situation, a thread waits on the locks container because it can't yet know whether the ultimate prize is indeed locked at that point in time. Once it gets the monitor on the locks container and it finds the resource locked, it waits and there is not a violation. The requirements state: "Any attempt to lock a resource that is already locked should cause the current thread to give up the CPU, consuming no CPU cycles until the desired resource becomes available." When the thread is notified, it starts all over again in its attempt to lock the resource so there is no violation, although there are some, not necessarily a lot of, wasted CPU cycles.
kktec<br />SCJP, SCWCD, SCJD<br />"What we observe is not nature itself, but nature exposed to our method of questioning." - Werner Heisenberg
I understand Derek's issue Phil: he's providing a Threadsafe implemention of the returned value. I'm just suggesting that this isn't really necessary, and doesn't actually solve a problem.
Regarding your tests: I have a suggestion for you that may seem a little strange, but bear with me. Try running the tests in a different order, and see if get different results.
Dear Philippe, i don't believe you are still here sorting out for everybody's sake.
Now, you are just perfectionist. Never mind...
Originally posted by Philippe Maquet:
Hi Max,
quote:
--------------------------------------------------------------------------------
I understand Derek's issue Phil: he's providing a Threadsafe implemention of the returned value. I'm just suggesting that this isn't really necessary, and doesn't actually solve a problem.
--------------------------------------------------------------------------------
I wrote above that Derek's issue had nothing had nothing to do with locking. I should have added and with threads neither.
Data should protect his cache, period.
quote:
--------------------------------------------------------------------------------
Regarding your tests: I have a suggestion for you that may seem a little strange, but bear with me. Try running the tests in a different order, and see if get different results.
--------------------------------------------------------------------------------
I get very comparable results. Now I noticed that with any order, there is small variations among them which is probably due to the random nature of such tests.
1. Your answer just demonstrated that twice you didn't understand my question, but it's not a problem anymore. I was questioning 1.1 (notifyAll() in lock() and unlock()) and you defended 1.2 (notifyAll() in unlock() only), solution I find now the best one as explained above.
kktec<br />SCJP, SCWCD, SCJD<br />"What we observe is not nature itself, but nature exposed to our method of questioning." - Werner Heisenberg
This is an interesting assertion. Does a hashmap, even a synchronized one, protect it's cache, period?
This is also interesting, and not consistent with my own data. What version of the jdk are you using? Can you simplify your tests, and post them? No need to provide locking code per se: any code the compares notify, notifyAll, or notifyAll + notifyAll will do.
Originally posted by Philippe Maquet:
Hi Max,
quote:
--------------------------------------------------------------------------------
This is an interesting assertion. Does a hashmap, even a synchronized one, protect it's cache, period?
--------------------------------------------------------------------------------
I don't understand what you mean. I think we don't talk about the same topic! I just focused on what Derek pointed out and I agree with him.
quote:
--------------------------------------------------------------------------------
This is also interesting, and not consistent with my own data. What version of the jdk are you using? Can you simplify your tests, and post them? No need to provide locking code per se: any code the compares notify, notifyAll, or notifyAll + notifyAll will do.
--------------------------------------------------------------------------------
I was talking about the second test (the one which elulated locks granting with the three techniques explained above). But'll I send you both of them anyway, but preferably this weekend (I'm dead-tired tonight ). I have to write to you anyway.
Best,
Phil.
Let me ask the question in a different way. What is the principle that leads you to believe that Data should protect it's cache? What existing problem are you solving here? And does that principle cause a conflict when applied to, say, a HashMap?
What is the principle that leads you to believe that Data should protect it's cache?
[Max]: What actual, existing, problem are you solving here?
What actual, existing, requirement are you implementing?
(...) 40 points are gone. To test this
hypothesis, I'd guess the following: those that used a factory and parceled
one unique Data on the server per client, had no multi-threading issues (except in the lock itself), thus, the probability of a high score was good. Those that had one instance of Data on the server, were faced with multi-threading, and if it is new to you, its very, very easy to make a mistake, and then not have it turn up in testing: 40 points gone.
That new kid is a freak. Show him this tiny ad:
Gift giving made easy with the permaculture playing cards
https://coderanch.com/t/777758/Gift-giving-easy-permaculture-playing
|