aspose file tools*
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes URLyBird Locking Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Certification » Developer Certification (SCJD/OCMJD)
Bookmark "URLyBird Locking" Watch "URLyBird Locking" New topic
Author

URLyBird Locking

Ta Ri Ki Sun
Ranch Hand

Joined: Mar 26, 2002
Posts: 442
I realise this subject has been dealt with many times, and I've just read a lot of interesting views on locking, but I need to ask some questions and clear it up for myself, instead of taking bits and pieces spread across countless threads, thanks in advance.

According to the javadoc a record is locked, yet I see many implementations that use a HashMap or similar, and lock the entire map, doesn't this go against the javadoc?
Locks a record so that it can only be updated or deleted by this client.

Locking the Map would force a client to wait when it's interested in a different record.

I also started with the Map, and have since got rid of it in favour of a HashSet since my interface does not include a cookie/clientId, I think I'll continue to use the Set but synchronize on a specific Integer object(recNo) in the Set, does this sound right?

For now assuming I leave my code as is, synchronising on lockSet,
does the lock method call notifyAll at all?
Or is the process as I understand it...

lock method:
synchronize lockSet
while locked, wait.
add recNo to lockSet

unlock method:
synchronize lockSet
remove recNo from lockSet
notifyAll

The RemoteDataAdapter then, in bookRoom, calls lock, then various validations such as availability, then update, and then unlock.
Ta Ri Ki Sun
Ranch Hand

Joined: Mar 26, 2002
Posts: 442
Hate to bump but I've had this project on ice for 2 years, just dusting it off now and hope to upload soon, can anyone off some input?
Paul Bourdeaux
Ranch Hand

Joined: May 24, 2004
Posts: 783
lock method:
synchronize lockSet
while lockSet contains recNo, wait.
add recNo to lockSet

unlock method:
synchronize lockSet
remove recNo from lockSet
notifyAll

I think you had it correct, but the wording in your psudo code was a little ambigious. I changed your lock method psudo code a little bit to reflect this. Here is an example using the new psudo code:

Thread A (TA) calls lock(recNo 1) -
- TA synchronized on lockSet
- TA checks to see if lockSet contains recNo 1 (it doesn't)
- TA adds recNo 1 to lockSet and exits the synchronized block

Now TA is free to go off and preform operations on recno1, which it has logically locked.

Meanwhile, Thread B (TB) calls lock (recNo 1) -
- TB synchronizes on lockSet
- TB sees that lockSet contains recNo 1 and waits...

Thread C (TC) calls lock (recNo 2) -
- TC synchronized on lockSet
- TC checks to see if lockSet contains recNo 2 (it doesn't)
- TC adds recNo 2 to lockSet and exits the synchronized block

Later, TA finished its work on recNo1, and calls unlock (recNo 1) -
- TA synchronizes on lockSet
- TA removes recNo 1 from lockSet
- TA calls notifyAll(), waking all waiting threads (Thread B) and exits the synched block

- TB, who is now awake again, is still in the synched block
- Because we used a while loop, TB checks again to see if lockSet contains recNo1 (now it doesn't)
- TB adds recNo1 to the lockSet and exits the synchronized block...
[ April 12, 2005: Message edited by: Paul Bourdeaux ]

“Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning.” - Rich Cook
Kang Wang
Greenhorn

Joined: Apr 08, 2005
Posts: 18
Hi Paul,

I agree with your pseudo code except that I have some doubt about what happens when Thread D(TD) tries to read recNo 1 while TA is locking recNo 1 and modifying it?

I think we can use another Hashtable/set, e.g. readSet, to track record numbers that are being read. Thus, when TD tries to add 1 to readSet, it will wait on lockSet since lockSet still contains 1. As soon as 1 becomes free again, TD will synchronize readSet and add 1 to it. Thread E(TE) will still be able to read record 1 because 1 is not in lockSet. However, Thread F(TF) will not be able to lock it because it checks readSet in a synchronized block and readSet contains 1.

Please advise.

Thanks guys.
Kang


No victory is more glorious than defeating myself.
Ta Ri Ki Sun
Ranch Hand

Joined: Mar 26, 2002
Posts: 442
urgh, thanks Paul, you've made me read the javadoc for wait, and I've found
relinquish any and all synchronization claims on this object.


My misunderstanding came from forgetting this, and I feared the worst.
Thanks a lot!
btw our code was the same, by while locked I meant I'd call isLocked(recNo), which does exactly what you posted, a check to see if the lockSet contains it.
Ta Ri Ki Sun
Ranch Hand

Joined: Mar 26, 2002
Posts: 442
Originally posted by Kang Wang:
Hi Paul,

I agree with your pseudo code except that I have some doubt about what happens when Thread D(TD) tries to read recNo 1 while TA is locking recNo 1 and modifying it?

I think we can use another Hashtable/set, e.g. readSet, to track record numbers that are being read. Thus, when TD tries to add 1 to readSet, it will wait on lockSet since lockSet still contains 1. As soon as 1 becomes free again, TD will synchronize readSet and add 1 to it. Thread E(TE) will still be able to read record 1 because 1 is not in lockSet. However, Thread F(TF) will not be able to lock it because it checks readSet in a synchronized block and readSet contains 1.

Please advise.

Thanks guys.
Kang



Nothing is done for read, no check to see if locked or anything else at all, it just reads.
The risk is ofcourse dirty reads but that's acceptable, and documented.
Ta Ri Ki Sun
Ranch Hand

Joined: Mar 26, 2002
Posts: 442
If anyone has any concurrency doubts, which I clearly still do, I'm finding this site to be very useful
Frans Janssen
Ranch Hand

Joined: Dec 29, 2004
Posts: 357
Hi Ta (I am not sure if that's your first name! ),

You may want to consider to store in the lockSet which client locked the record, so that you can check in your modify and unlock methods if the caller is indeed the lock owner.

Also, after Thread B is woken up from waiting, it will probably want to check if the record it is waiting for still exists (it might have been deleted by Thread A). I strongly suspect that failing to do so is a recipe for the mysterious 44/80 locking score.

Frans.


SCJP 1.4, SCJD
Ta Ri Ki Sun
Ranch Hand

Joined: Mar 26, 2002
Posts: 442
Originally posted by Frans Janssen:
Hi Ta (I am not sure if that's your first name! ),


It's what my mom calls me, should do fine


You may want to consider to store in the lockSet which client locked the record, so that you can check in your modify and unlock methods if the caller is indeed the lock owner.

Right back to the reason I started the LockManager, I'm scratching my head over this one quite a bit.
lock(int recNo) says
only be updated or deleted by this client
, which ofcourse means I have to implement the clientId in some form, however overloading this method or implementing in LockManager still does not fulfill the contract of lock, as lock will never be able to live up to the javadoc. I suppose I should implement in LockManager and document in choices.txt


Also, after Thread B is woken up from waiting, it will probably want to check if the record it is waiting for still exists (it might have been deleted by Thread A). I strongly suspect that failing to do so is a recipe for the mysterious 44/80 locking score.

javadoc for read in DbMain says
Thrown when record not found or record
* is marked as deleted

so when bookRoom reads the record after aquiring lock it will have checked this.

Frans, thank you much
Frans Janssen
Ranch Hand

Joined: Dec 29, 2004
Posts: 357
Originally posted by Ta Ri Ki Sun:


quote:
Thrown when record not found or record
* is marked as deleted


so when bookRoom reads the record after aquiring lock it will have checked this.


My instructions required RecordNotFoundException to be thrown also from the lock method if the record did not exist. But if your instructions don't require this, you can of course ignore my comment.

Frans.
Ta Ri Ki Sun
Ranch Hand

Joined: Mar 26, 2002
Posts: 442
Originally posted by Frans Janssen:


My instructions required RecordNotFoundException to be thrown also from the lock method if the record did not exist. But if your instructions don't require this, you can of course ignore my comment.

Frans.


You're right, I thought I had that well covered, thanks for the extra marks
Ta Ri Ki Sun
Ranch Hand

Joined: Mar 26, 2002
Posts: 442
Originally posted by Ta Ri Ki Sun:


You're right, I thought I had that well covered, thanks for the extra marks



This opened up a whole new can of worms for me, because not only does lock need to check if deleted, so does unlock.
So the client gets a lock, deletes the record, unlocks and finds itself catching an exception, something doesn't seem right here.

Back to lock though, seeing as any isDeleted(recNo) implementation would involve reading the record, I'm sure I can skip such a method and simply attempt to read the record as this will throw the exception anyway correct?
Paul Bourdeaux
Ranch Hand

Joined: May 24, 2004
Posts: 783
This opened up a whole new can of worms for me, because not only does lock need to check if deleted, so does unlock.
So the client gets a lock, deletes the record, unlocks and finds itself catching an exception, something doesn't seem right here.
This was a topic of debate some time ago, I will try and see if I can find the link again.

Basically, when you delete a record, you only need to change the deleted flag of the record to indicate that it has been deleted. (At least that is how is was in my version) If a record is still deleted, it still exists in the database - it has just been deleted.

When my lock method was entered, it would check first to see if the record exists, i.e. makes sure that if the recNo passed was 52, the datafile itself was large enough to contain at least 52 records. if not it would throw Next it would check to see if the record had been deleted, and if so throw

In my unlock method, I only checked to make sure that the record existed. That is to say while you cannot lock a deleted record, it is perfectly legal to unlock one. It seemed to work pretty well for me.
Frans Janssen
Ranch Hand

Joined: Dec 29, 2004
Posts: 357
Originally posted by Paul Bourdeaux:
In my unlock method, I only checked to make sure that the record existed. That is to say while you cannot lock a deleted record, it is perfectly legal to unlock one.


Yes, I did the same. I feel that since you must lock a record to delete it, one must be allowed to unlock that record even after it has been deleted. Otherwise the symmetry would be broken where sometimes a record must be unlocked and sometimes it must not. That just does not feel right.

Frans.
Kang Wang
Greenhorn

Joined: Apr 08, 2005
Posts: 18
Hi Paul and Frans,

Can we throw a RecordNotFoundException from the method that follows the lock method? Then we can save some I/O overhead since that method will have to open the file anyways. What's the advantage of throwing this exception from lock()?

Thanks
Kang
Paul Bourdeaux
Ranch Hand

Joined: May 24, 2004
Posts: 783
first, it is usually a required exception in the interface provided... so we don't have much of a choice!

Second, you don't want to have the possiblity of many clients waiting for a lock on a record that simply doesn't exist. For example:
  • Client A attempts to lock recNo 30. The lockSet does not contain 30, so it adds it and exits the synched block. recNo 30 is effectively locked.
  • Client B tries to lock recNo 30. It is already in the lockSet, so Client B waits...
  • Client C tries to lock recNo 30. It is already in the lockSet, so Client C waits...
  • Client D tries to lock recNo 30. It is already in the lockSet, so Client D waits... (and so on and so on...)
  • Meanwhile, Client A finally gets around to doing some work on recNo 30 and discovers.. Oops, it has been deleted. It throws the exception (and hopefully calls notifyAll()).
  • Client B, C, D... all wake up and take their turns discovering that the record is deleted...
  • If the check was made in the lock method, the clients would never have waited, and they would have gotten the exception much quicker.

    <<I removed my second scenario due to a mistake in it - sorry!>>
    [ April 13, 2005: Message edited by: Paul Bourdeaux ]
     
    Consider Paul's rocket mass heater.
     
    subject: URLyBird Locking