aspose file tools*
The moose likes Developer Certification (SCJD/OCMJD) and the fly likes Lock/Unlock mechanism - another view 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 "Lock/Unlock mechanism - another view" Watch "Lock/Unlock mechanism - another view" New topic
Author

Lock/Unlock mechanism - another view

Jim Janssens
Ranch Hand

Joined: Sep 24, 2004
Posts: 210
Yesterday I got another view on the locking mechanism and I think I finally had a total solution of which I was 100% happy. Until this morning I found a 'little' problem of which I cannot find a decent solution. Let me first explain what I have now, since it is crucial to understand what I was trying to change, maybe it is also interesting for others out there:

My current implementation uses a queue for writing operations on the same record. So there is no chance that 2 threads can update the same record at the same time, since they have to wait there turn. There is no unfair treatment (no notifyAll()) and it does not consume cpu cycles, since there is only one thread notified at a time using its lock object, it WILL receive the lock. Nice !

Besides the write, there is also the read. A read does not require a lock, meaning that reading should go everytime by every thread on every record number. If thread A is reading recordnumber one, thread B should also be able to read recordumber one at the same time.

Now, there is only one more issue left. Reading on a record number should not be allowed when there is currently a thread writing to it, and also vica versa. Allowing this will end up in reading unpredictable data. I solved this by placing "syncrhonized" on the readRecordFromDatabase() and writeRecordToDatabase() of my data class. These methods do the real databasefile access thing (using RandomAccessFile) . I will call them read() and write() from now on , to make it easier. These 2 methods are the only two syncrhonized methods I have in Data class, the rest of the syncrhonization occurs on objects in the 'DataManager' class which handles the logical locking.

When one thread gets the logical lock and it is able to write, no other thread can read since it is in the syncrhonized write method. So the read will wait behined scenes until the write finishes. Also the other way around: a thread is readin a record and in the mean time another thread gets the logical lock so it starts writing. Again, this thread has to wait until the other tread leaves the read method. nice !

So, this should cover all problems :

-A 'no cpu cycle consuming' queue mechanism when multiple threads want to write to the same record number, which promotes fair handling (first come first serve) .

-Reads do not require a lock, meaning that multiple reads on the same record number will not be queued and go at the same time.

-Safety on the lowest read/write method by using synchronized which prevents that writing and reading goes at the same time.

Ok, the problem in this lovely story is the synchronized database read/write which do the actual file access. Synchronizing them sure solves my problem, but it seems not right after I evauluated some things.

Supose you have 10 threads only doing reads. Then they are NOT doing that simultaneously, because of the synchronized read ! 9 threads are waiting until the first thread leaves the read. After that, 8 threads are waiting until thread 2 leaves the read, and so on. There is no reason why they should wait. Also, supose you have 10 threads writing each to a different record number, then each of them will receive a logical lock at the same time (because it are all different record numbers) . But, the actual write will go sequential , just because of the synchronized, while this SHOULD go simultaneously since they are all different record numbers ! *sigh*

Ok, I decided to drop the "synchronized" on the read() and write() and to build a mutex system based upon logical read/write locks. It takes a lot of thinking, but eventually it is possible. This system provides me:

- simultaneous reading of records
- simultaneously writing of different records
- simultaneously writing and reading if the record numbers are different
- queue based waiting when multiple writes occur on the same record number
- read waits on a record number that is currently locked for writing. After the unlock occurs, all the waiting reads on that record number will all be notified so they can continue reading simultaneously.
- writes to a certain record number wait when there is currently being read from that record number. They will be notified when the read ends.

Now my real question:

In the read() and write() methods I'm using RandomAccessFile. I use the seek() method to move the file pointer. But I have only once RandomAccessFile instance. So, if 10 threads are reading at the same time, they will be all moving the same pointer on the same RandomAccessFile, I suspect ? Earlier this was no issue because of the synchronized read/write...

Is this true ? And how can I solve this ? Sure, I can bring down the RandomAccessFile from class variable to local variable in the read and write method. But isn't it expensive to create a new RandomAccessFile instance everytime a record is read ? ...

Thanks.
Andrew Monkhouse
author and jackaroo
Marshal Commander

Joined: Mar 28, 2003
Posts: 11509
    
  95

Hi Koen,

In the read() and write() methods I'm using RandomAccessFile. I use the seek() method to move the file pointer. But I have only once RandomAccessFile instance. So, if 10 threads are reading at the same time, they will be all moving the same pointer on the same RandomAccessFile, I suspect ? Earlier this was no issue because of the synchronized read/write...

Is this true ? And how can I solve this ? Sure, I can bring down the RandomAccessFile from class variable to local variable in the read and write method. But isn't it expensive to create a new RandomAccessFile instance everytime a record is read ? ...


You are correct - having 10 threads all accessing the one RandomAccessFile in unsyncronised code can cause major problems, including a totally corrupted data file.

You might want to consider having a pool of RandomAccessFiles: this way you avoid the expense of creating a new RAF each time, while simulataneously increasing concurrency.

Some things to think about though:
  • This extra concurrency is not really required by the instructions (and you don't get extra marks for extra work)
  • The code will be slightly more complex / harder for a junior programmer to understand
  • This aproach relies on the underlying operating system allowing multiple writable file descriptors on the one underlying file. I have tested this under MS Windows 95 ... 2000, Linux, and Solaris without problems. I believe Macintosh should also handle this. However you would still need to write your pool in such a way that you can handle the cases where the Operating System doesn't support it.

  • Regards, Andrew


    The Sun Certified Java Developer Exam with J2SE 5: paper version from Amazon, PDF from Apress, Online reference: Books 24x7 Personal blog
    Jim Janssens
    Ranch Hand

    Joined: Sep 24, 2004
    Posts: 210
    Yeah ok, actually I don't want to make the matters complex. But if I get the feeling my code is not 100% ok I feel bad, especially if I consider the fact that I could loose points. I don't feel bad if I'm losing points for things I didn't see or knew, but I do know that the original system I had was not entirely correct.

    Anyway, you think that if I synchronize on the read/write methods (which use the RAF) I will not be losing points ?
    Andrew Monkhouse
    author and jackaroo
    Marshal Commander

    Joined: Mar 28, 2003
    Posts: 11509
        
      95

    Hi Koen,

    Anyway, you think that if I synchronize on the read/write methods (which use the RAF) I will not be losing points ?


    I think as long as you explain what you have done and why you have done it, you should not loose points for this.

    Regards, Andrew
    Jim Janssens
    Ranch Hand

    Joined: Sep 24, 2004
    Posts: 210
    Ok. One last tougth:

    If you synchronize on the read() and write() methods, what is the added value of the entire locking mechanism then?

    I you do synchronize like this, you will never have a read that occurs while writing a record, so reading incorrect data is hereby no issue. Also the other way around, if you are reading a record, no thread is ablo to write to the same (or other) record as long as the read isn't finished. A single read or write is hereby an atomic operation which must be completed before another read or write starts (regardless of the record number) .

    I feel that they want to push you in a certain direction by making the locking mandatory (as described in the interface).

    Is there a way that other people who have done this exam give some input on this ? ...
    Andrew Monkhouse
    author and jackaroo
    Marshal Commander

    Joined: Mar 28, 2003
    Posts: 11509
        
      95

    Hi Koen,

    The logical record locking is used in a totally different manner than the physical file locking that occurs when reading/writing.

    You need the logical record locking when you are booking a record. When a client attempts to book the record, they need to confirm that it is still available to be booked - that is, that no other client has booked it since they did the last search. If all you are relying on is the physical file locking then there is always the chance that some other client will book the record between the verification and the booking:
  • Client A does searches
  • Client A chooses record 'x' for updating
  • Client A verifies that record 'x' is still available
  • Client B books record 'x'
  • Client A books record 'x'

  • Note that you cannot use Data's update() method to verify whether the record is still available - the update() method should be able to update any field in any way (including, potentially, removing the client ID). So you cannot put business logic in there.

    Therefore at a higher level, you need a "book" method, which will logically lock the record, after which it can do it's verification and update safely, knowing that no other client can be doing an update (since no other client can get the logical lock).

    Regards, Andrew
    Jim Janssens
    Ranch Hand

    Joined: Sep 24, 2004
    Posts: 210
    The update method in my Data class allows updating at any time by any thread. It is the update() method in the 'Database' class which takes care of locking and the consistency check.

    I feel (and correct me if I'm wrong) that you should not be able to update a record if the current record you used to base the update on is out of date. Disregarding the field. well especially for the book field , but I let it count for the other fields also.

    Eventually I will have a book method in my business facade, but that will do nothing more then call the update with the instructions to only change the book field to the booked value. If the record has allready been booked, the update method will throw an out-of-synch exception.
     
    I agree. Here's the link: http://aspose.com/file-tools
     
    subject: Lock/Unlock mechanism - another view