Sorry I could resist that. Seriously what is the general consensus around what the topic of this message **REALLY** means? For those of you don't know, that text that is common to many (all?) of the SCJD certification exams DB.java interface classes provided by Sun.
What this appears to mean is that I have to force the client to hang as they wait for the lock to be released by another client attempting to lock() the same record. This would seem to prevent something along the lines of displaying a JOptionPane stating:
"The record you have selected to book/modify/delete is currently in use by another User. Please try again later."
The reason that is prevented is that the full text states:
In order to maintain "consumes no CPU cycles" to the letter it would seem that you would have to immediately throw the thread into a wait(). This prevents the above dialog box functionality as there is no way to transfer control back to the client short of having a timeout on the method call to lock(). A more desired option would be to return -1 as the cookie value thus allowing the user to have discretion as to whether he wanted to continue to obtain the lock.
All of this assumes that if a user wants to update() a record by either booking it or modifying the values that he should be able to hold the record for an extended period of time as he redefines or enters information. Another alternative would be to allow multiple to users to **THINK** that they are modifying the record but when they call click "Book" the server takes the first and sends an error message to the other users.
Your guidance, as always, would be much appreciated.
[ November 16, 2007: Message edited by: Thomas Hubschman ]
[ November 16, 2007: Message edited by: Thomas Hubschman ] [ November 16, 2007: Message edited by: Thomas Hubschman ]
I think perhaps you're taking the no cpu cycles demand a bit too literally. I think they simply mean they expect you to block the thread rather than have it in some sort of while locked loop. At which point you actually put the thread in a wait state is not that important as long as the locking mechanism is functionally sound.
Also, it's generally not advisable to lock a record at the point where a user starts to enter information and unlock it when they're done and the record is saved. Instead, let the user enter the new information and then let them confirm persisting the data. Only at that point should you lock, update and unlock the record.
Hope that helped.
posted 11 years ago
Thanks. Based on what you said I am not sure if the below is overkill. It sounds like returning a '-1' as the lock cookie would be fine too. However....
Since that post earlier today I have implemented record locking as follows: The current thread, which would like to lock the record, but cannot because it is already locked, goes into the wait state until the lock is released by the thread which owns the lock. When the lock is released the first thread in a queue for that record lock is awoken via a call to notify(). I actually used the thread object itself as the monitor. This allows me to only pop the first thread that requested this lock (in a situation where there are multiple threads waiting for a single lock) out of a queue of a threads waiting to lock the record. So far so good.
Now this next part (your second paragraph), would what you described not be really annoying for the end user if his changes had to be dumped because the record was in the process of being edited by another user? (And I am thinking that annoyance is bad for the 40 point User Interface and 100 point General Consideration sections).
I was thinking of having a 4 stage process of:
1. Read Record 2. Lock Record 3. Edit Record 4. Submit Record
A request to lock would return fairly immediately. Then the user would have a bit of time to edit the record without fear of loosing the right to reserve that resource. Finally they would submit their changes secure in the knowledge that no one else booked it while they were entering the details.
(BTW - I am on URLyBird 1.1.1)
In the case of the user waiting for the lock, they would have the ability to continue to wait for the record lock, or to hit cancel to try their luck locking a different record.
Personally I would far prefer to have a composite set of actions on the server, (i.e. lock(), update(), unlock()) with the client just calling book(). I am thinking based on your message that perhaps a few refused updates now and then is not such a travesty.
Thanks again for your help.
Tom [ November 16, 2007: Message edited by: Thomas Hubschman ]
I think you should take the "the current thread gives up the CPU and consumes no CPU cycles consumes no CPU cycles until the record is unlocked." very literally.
(even though you don't like the consequences of it)
I know people who failed, because they implemented a time-out.
R van Vliet
posted 11 years ago
Thomas, The issue here is mostly a matter of picking the lesser of two evils. In my approach editing and updating a record would look something like this (very pseudo) :
As you can see, there's only a very short time during which the record is actually locked, and as the exception part shows a concurrent modification problem is definately in the cards.
However, this is only a problem if two users want to edit the same record, and if that situation occurs, locking the record before allowing an edit will still disallow the second user to edit it, only for a significantly longer amount of time. And dont forget the end result (the second user attempting to overwrite a record he or she wasn't previously aware of) is the same. Both situations require waiting for the record to be unlocked, reading the newly modified record data, present it to the user and optionally ask if the user wishes to commit their record or keep the record data as is.
In short, the functional difference is mostly between telling the user "this record is being modified, you cannot edit it" and "this record has been modified since you started editing", the technical difference is that with my approach records spend a lot less time being locked.
I think you misinterpreted my reply. I didn't say an approach that doesnt give up CPU is valid. I meant Thomas' assumption here "What this appears to mean is that I have to force the client to hang as they wait for the lock to be released by another client attempting to lock() the same record" isn't correct. The client doesn't have to hang at all, as long as the thread that attempted to lock the record gives up CPU until it can acquire the lock.
I do not use other threads than the main one until now. How should I call wait()? Where would you make a Thread? What shall happen if the lock is released? Call notify()? What if the record has been deleted? Thank you.
Thanks again. Let me extend that example a bit in a multi-user environment. The below is based on what you suggested R.
Customer Service Agent (CSA) 1 is on the phone with Mr. Vliet ------------------------------------------------------------- Time 0: Vliet asks CSA 1 to see what records are available for 12/31/07. Request is sent to server. Time 2: 25 records match this date Time 4: 25 records are read by the DB Time 6: 25 records are sent over the wire to the Swing Client Time 8: 25 records are populated in a JTable Time 10: CSA 1 asks Vliet which one he wants. Time 12: Vilet selects record X. Time 14: CSA 1 updates the "owner" field of record X with "Vliet" Time 16: CSA 1 sends the updated record over the wire to the server Time 18: The server locks, verifies no other edits, and updates the record Time 20: CSA 1 confirms that Vliet has the record.
Customer Service Agent (CSA) 2 is on the phone with Mr. Scheltinga ------------------------------------------------------------------ Time 1: Scheltinga asks CSA 2 to see what records are available for 12/31/07. Request is sent to server. Time 3: 25 records match this date Time 5: 25 records are read by the DB Time 7: 25 records are sent over the wire to the Swing Client Time 9: 25 records are populated in a JTable Time 11: CSA 2 asks Scheltinga which one he wants. Time 13: Scheltinga selects record X. Time 15: CSA 2 updates the "owner" field of record X with "Scheltinga" Time 17: CSA 2 sends the updated record over the wire to the server Time 19: The server locks, verifies no other edits (EXCEPTION THROWN) Time 21: CSA 2 tells Scheltinga that he needs to select a new record.
So for Mr. Scheltinga and for CSA 2 their time was wasted from Time 13 through Time 21. It would be nice if there was away to prevent this from occurring. I dunno, maybe this is too difficult to implement in this system because of the fixed interface. Or maybe this issue is unresolvable regardless of the interface due to the nature of the scarcity of the resources and the time required for human contact.
[ November 18, 2007: Message edited by: Thomas Hubschman ] [ November 18, 2007: Message edited by: Thomas Hubschman ]
Wow Thomas, that's certainly a thorough example and ofcourse you're absolutely right that that is an issue with the approach I chose (or should I say, am currently erring towards).
Still, I believe it's preferable over the alternative you presented. Another reason for this is that with your alternative the client has to tell the server a record has to be locked. In other words, the client has to be aware of your record locking scheme. In my facade the client simply attempts a single bookRoom call, which may throw an exception. The client is not required to manually lock records at any point.
Also, I think solving this problem to begin with might be outside the scope of this assignment. Perhaps Sun just expects us to implement the locking correctly and present the user with feedback if such a synchronization issue occurs. What do you think?
[Thomas Hubschman :]   In order to maintain "consumes no CPU cycles" to the letter it would seem that you would have to immediately throw the thread into a wait(). At an absolute minimum, there has to be a test reg a, reg b jnz in a wait(), and that would be a grossly oversimplified snippet. The processor would have to do some work to put up the JOptionPane.
I think the designers of the certification are reasonable here in claiming that there are no processor cycles taken up - but that is in my opinion a consequence of having to abstract away implementation details.
update() should either succeed or rollback, it would seem that trying a lock on a record should wait until the user has completed an entry ... but it is just a question of how big the application is and where it runs and so on. It just has to be described in the context of a real-world setting. A cargo shipping app is different from an order-inventory app.
[Thomas Hubschman :]   Personally I would far prefer to have a composite set of actions on the server, (i.e. lock(), update(), unlock()) with the client just calling book(). I am thinking based on your message that perhaps a few refused updates now and then is not such a travesty
A few refused updates cannot result in a tragedy, if it will the entire application has to be redesigned in a very robust manner. My suggestion on the face of it, simplified, is to implement a static synchronized add method that puts things in a static Vector, checking first on Schedulability Bounds of a Static Scheduler. In other words a fast if(vector.size() < capacity)vector.add((Object) request); does not cost enough processor cycles to be worth the risk of not using it.
Such a static synchronized update() can fail and return the error message if needed. If this is not acceptable, then hanging the client is no solution either.
"The differential equations that describe dynamic interactions of power generators are similar to that of the gravitational interplay among celestial bodies, which is chaotic in nature."
posted 11 years ago
Thank you for the link. I didn't see any reason why using multiple threads until now. And to be honest I'm not really convinced yet. Multithreading can be dangerous, and one has to be careful when using it. My question was where does it make sense to use threads in UrlyBird and why?
R van Vliet
posted 11 years ago
Multi-threading is not necessarily more dangerous, it simply requires developers to pay attention to some of the pitfalls that come with it. Trying to keep a large application single threaded comes with it's own set of "dangers" so it really shouldnt be a choice based on risk reduction alone.
Now, I believe it is almost impossible to correctly implement URLyBird without multithreading. A number of reasons :
- Server side connection IO is almost always multi-threaded. If you use RMI it is by default, and if you use sockets the only clean way to do single thread IO is using the NIO packages, and even there it's usually best to have worker read/write threads.
- The GUI. You dont want your user interface to block every time you do some (potentially lengthy) operation. Consider for example clicking the book button and have the interface freeze completely until the booking succeeded.
- The assignment specifically states you having to implement a locking solution. This implies you need to create a situation where there are actually multiple threads that may compete for such locks. In other words, desperately trying to stick to a single thread may be the cause of an automatic failure.
I'm sure there's more reasons. In short I think the instructions imply that it must be a multi-threaded solution, and I think the problems you have to solve in the assignment suggest using more than one thread is actually better by design as well.
Basically I'm 100% sure a multi-threading approach is preferred if not mandatory. So best case scenario is losing a lot of points (all the locking ones i can imagine), but most likely it's an automatic fail.