• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • paul wheaton
  • Paul Clapham
  • Ron McLeod
Sheriffs:
  • Jeanne Boyarsky
  • Liutauras Vilda
Saloon Keepers:
  • Tim Holloway
  • Carey Brown
  • Roland Mueller
  • Piet Souris
Bartenders:

Locking

 
Ranch Hand
Posts: 52
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
How extensive the locking system supposed to be? It carries a full 80 marks? I tried to implement it with wait/notify. The code is very compact (around 100 lines)
 
Ranch Hand
Posts: 197
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Well mine is just 64 lines of actual code.

Robert got 80/80 with about the same amount of code.
Everywhere you read on this site, people say keep it simple in all aspects of design, but with locking this seems to have even more emphasis.

That should make you feel better.
 
Ranch Hand
Posts: 531
1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Surya Kumar:
How extensive the locking system supposed to be? It carries a full 80 marks? I tried to implement it with wait/notify. The code is very compact (around 100 lines)



As long as it locks in 100 percent of cases, it should be fine, whatever the number of lines. My project contains the following scheme:

thread locks the Data class.
thread looks if a record is locked. if yes, it releases the lock on Data class and waits, and periodically checks if the record is still locked (lock Data, check, unlock Data).
thread locks the record
thread releases lock on Data class.

Similar operation is with unlock method.

Unless I am missing something, that is all that will fulfill the requirement. As long as any io operation is called from within synchronized chunk of code (synchronized on the Data class), io can be thread-unsafe, it won't matter.

I also decided to keep it simple. Previously, I thought of implementing a Cache class to hold records. Now I will only implement a private Hashmap. It should be enough.
 
Ranch Hand
Posts: 41
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
100 lines of code for synchronization? In my DBMain interface the methods look like this:
public void lock(int recNo) throws RecordNotFoundException;
public void unlock(int recNo) throws RecordNotFoundException;
public boolean isLocked(int recNo) throws RecordNotFoundException;

It is not possible to make sure that the user that have locked the record is the same that calls update/delete or create. I have decided to make it optional to use the locking in the interface and it is possible to call update/delete on a record that is not locked.
 
author and jackaroo
Posts: 12200
280
Mac IntelliJ IDE Firefox Browser Oracle C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Mike,

Welcome to JavaRanch and this forum.

Originally posted by Mike Vess:
It is not possible to make sure that the user that have locked the record is the same that calls update/delete or create. I have decided to make it optional to use the locking in the interface and it is possible to call update/delete on a record that is not locked.



Why do you think it is not possible to do this?

Are you sure you can get away with this? In my instructions, in the comments above the lock signature it states:



(Emphasis is mine)

Now each person potentially has a different assignment, even if they both have "URLyBird" or "Bodgitt & Scarper". But even so, most assignments seem to have some implicit instructions requiring the lock owners to be tracked. You really need to be absolutely certain that there is nothing in your instructions that might require tracking ownership of locks, or you could end up loosing marks for this section.

Some things I would look for in locking code:
  • Can anyone unlock a locked record, or can only the locker unlock it (hint, if you are using thread number with RMI, then you might be in for a nasty surprise)
  • Can more than one client simultaneously gain a lock on a particular record (hint: are there any race conditions?)
  • Can a deadlock occur? How do you handle this (hint: the answer might not be "write code")
  • Can locking be bypassed / is it mandatory in your instructions
  • Is high concurrency achievable? That is, at one end of the scale, if you simply put your entire book() method into a synchronized block, you might not need the locking code at all, however only one client would ever be able to book a record at any given time. At an intermediate level, having only one thread woken up whenever a lock is released could mean that a thread may end up waiting needlessly for a lock that has already been released (yes, it will get woken eventually). A better solution would allow for fast, concurrent updates.
  • Do you unnecessarily lock records (is there any need to lock a record for read?)
  • Do you unnecessarily wake up waiting threads?


  • There are other points I could make, but there is another topic I want to post in, and I need to get to work .

    Regards, Andrew
     
    Anton Golovin
    Ranch Hand
    Posts: 531
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Mike Vess:
    100 lines of code for synchronization? In my DBMain interface the methods look like this:
    public void lock(int recNo) throws RecordNotFoundException;
    public void unlock(int recNo) throws RecordNotFoundException;
    public boolean isLocked(int recNo) throws RecordNotFoundException;

    It is not possible to make sure that the user that have locked the record is the same that calls update/delete or create. I have decided to make it optional to use the locking in the interface and it is possible to call update/delete on a record that is not locked.



    implementing a private method isLockedByCaller(int recNo, long cookie); should solve the identification issue.

    then, to determine if it is ok to call those methods, I would call for locking:

    while (isLocked(recNo) & !isLockedByCaller(recNo, cookie)) {
    try {
    this.notifyAll();
    this.wait();
    }
    catch (InterruptedException interrEx) {
    continue;
    }
    }

    for update, delete, etc.:

    if(isLocked(recNo) & isLockedByCaller(recNo, cookie)) {
    // do stuff
    }
    [ August 25, 2004: Message edited by: Anton Golovin ]
     
    Anton Golovin
    Ranch Hand
    Posts: 531
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
  • Can anyone unlock a locked record, or can only the locker unlock it (hint, if you are using thread number with RMI, then you might be in for a nasty surprise)


  • RMI is not guaranteed to use the same thread for the same client for all calls the client makes.
  • Can more than one client simultaneously gain a lock on a particular record (hint: are there any race conditions?)


  • a race condition is a situation when there is ambiguity as to the order of execution between differing threads, which may result in different outcomes time after time. Locking an object down eliminates it completely.
  • Can a deadlock occur? How do you handle this (hint: the answer might not be "write code")


  • Lock things in exact order. Catch InterruptedException in a way which gets your thread to give up control. Use IO carefully. Unlock things in exact opposite order they were locked.
  • Can locking be bypassed / is it mandatory in your instructions


  • Completely mandatory and worth 80 points. It also makes you cache your data, because otherwise locking the file down would obviate locking as a necessity in the assignment.
  • Is high concurrency achievable? That is, at one end of the scale, if you simply put your entire book() method into a synchronized block, you might not need the locking code at all, however only one client would ever be able to book a record at any given time. At an intermediate level, having only one thread woken up whenever a lock is released could mean that a thread may end up waiting needlessly for a lock that has already been released (yes, it will get woken eventually). A better solution would allow for fast, concurrent updates.


  • would this be notify vs. notifyAll issue? If so, notifyAll is the correct method to wake threads up. Contentions times will be an issue, but IMHO, they are unavoidable. If a better way exists, please let us know - always glad to learn something about threading. It's just a pretty hard topic, overall, IMHO.

  • Do you unnecessarily lock records (is there any need to lock a record for read?)


  • There seem to be two variants here. Reusing deleted records or not. If not, the only operation your code will ever do on an existing record is change the flag. However, supposing you wish to read a record, you may be superceded by one thread deleting that record and another writing a new record in its place after your tell your code to read the record but before it does. So it is a must to lock, and no way to know if the above situation occurred if it is not locked.

    If not reusing deleted records, and your code being absolutely proof against trying to read mint-new records, you don't have to lock, provided your read method checks the flag value for deletion. But you do have to lock not the record but the file.
  • Do you unnecessarily wake up waiting threads?


  • Contention time may be dramatic But I would still call notifyAll. Probably there is a better way, but I do not have the experience to devise it.



    Sorry to cut in. I was kind of testing myself on the understanding of this very important points. Hope someone else will find my musings a bit useful, too.
    [ August 25, 2004: Message edited by: Anton Golovin ]
     
    Mike Vess
    Ranch Hand
    Posts: 41
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    implementing a private method isLockedByCaller(int recNo, long cookie); should solve the identification issue.



    My interface provided by sun don�t have a cookie that makes it possible to identify which client that have loocked the record.

    How can i possible tell that it is the same client that have locked the record that then calls update/delete? The thread number is unusable when i use RMI.
     
    Ranch Hand
    Posts: 64
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Mike,

    Do you have any object unique to each client who connects to your server, within the server?

    Steven
     
    Mike Vess
    Ranch Hand
    Posts: 41
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I does not matter weather or not i have an unique object for each client that connects to the server. The problem is that the interface provided by sun not makes it possible to make sure that it is the same client that lockes the record that then updates or deletes it.

    public void lock(int recNo) throws RecordNotFoundException;
    public void unlock(int recNo) throws RecordNotFoundException;

    public void update(int recNo, String[] data) throws RecordNotFoundException;

    In the update method i can�t tell if it is the same client that have called the lock method and another question is what i should do in update if the record not is locked? Call lock method if it is not locked or throw RecordNotFoundException (which would be silly) or throw a RuntimeException (which also would be silly) or just ignore if the record is locked or not and just do the update.
     
    Anton Golovin
    Ranch Hand
    Posts: 531
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Mike Vess:
    I does not matter weather or not i have an unique object for each client that connects to the server. The problem is that the interface provided by sun not makes it possible to make sure that it is the same client that lockes the record that then updates or deletes it.

    public void lock(int recNo) throws RecordNotFoundException;
    public void unlock(int recNo) throws RecordNotFoundException;

    public void update(int recNo, String[] data) throws RecordNotFoundException;

    In the update method i can�t tell if it is the same client that have called the lock method and another question is what i should do in update if the record not is locked? Call lock method if it is not locked or throw RecordNotFoundException (which would be silly) or throw a RuntimeException (which also would be silly) or just ignore if the record is locked or not and just do the update.



    The interface does not, sure. But your implementation of lock and unlock and update methods in Data class may call private methods of same class, which are not exposed via your interface. There are no problems identifying the correct client.
     
    Mike Vess
    Ranch Hand
    Posts: 41
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    The interface does not, sure. But your implementation of lock and unlock and update methods in Data class may call private methods of same class, which are not exposed via your interface. There are no problems identifying the correct client.



    What does private method help me with? How can you say there is no problem identifying the client when the client only uses the methods defined in the interface?
     
    Mike Vess
    Ranch Hand
    Posts: 41
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    One possible solution whould be that all Clients have an own instance of Data.class and then have static methods for file access but that solution i dont like at all.
     
    Anton Golovin
    Ranch Hand
    Posts: 531
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Mike Vess:


    What does private method help me with? How can you say there is no problem identifying the client when the client only uses the methods defined in the interface?



    Consider this code:

    You have three methods:

    public void unlock(int recNo, long cookie) { ... } // defined by interface
    private boolean isLocked(int recNo) { ... } // defined by you
    private boolean isLockedByCaller(int recNo, long cookie) { ... } // defined by you.

    ==========================================================================

    private Map locked = new HashMap();

    public synchronized void unlock(int recNo, long cookie) {

    final recordNumber = recNo;
    final long cookieValue = cookie;

    if(!isLocked(recordNumber)) {
    throw new SecurityException("Record " + recordNumber + " is not locked.");
    }
    if (isLockedByCaller(recNo, cookieValue)) {
    locked.remove(new Integer(recordNumber));
    }
    else {
    throw new SecurityException("Record " + recordNumber + " is not locked by client.")
    }
    }
    [ August 26, 2004: Message edited by: Anton Golovin ]
     
    Mike Vess
    Ranch Hand
    Posts: 41
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    My interface looks like this:

    public void lock(int recNo) throws RecordNotFoundException;
    public void unlock(int recNo) throws RecordNotFoundException;

    Note that there is NO COOKIE value. Some of my collegies have another version of the interface with a cookie value but my interface don�t. If i had the version with a cookie value i would have no problem at all to solve to locking issue.
     
    Anton Golovin
    Ranch Hand
    Posts: 531
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Mike Vess:
    My interface looks like this:

    public void lock(int recNo) throws RecordNotFoundException;
    public void unlock(int recNo) throws RecordNotFoundException;

    Note that there is NO COOKIE value. Some of my collegies have another version of the interface with a cookie value but my interface don�t. If i had the version with a cookie value i would have no problem at all to solve to locking issue.



    I see. Then the only way is to guarantee that a client calls unlock(recNo) only after he calls lock(recNo), where recNo is one and the same.

    so in your business code, it would look something like this:

    DB db = new Data(...);

    public String book(int recNo) {

    final int recordNumber = recNo;
    db.lock(recordNumber);
    db.update(recordNumber, ...);
    db.unlock(recordNumber);


    }

    That final keyword will make your intention very clear, IMHO.
    [ August 26, 2004: Message edited by: Anton Golovin ]
     
    Mike Vess
    Ranch Hand
    Posts: 41
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator


    public String book(int recNo) {

    final int recordNumber = recNo;
    db.lock(recordNumber);
    db.update(recordNumber, ...);
    db.unlock(recordNumber);
    }



    This solution will not work because what happens if there is an error in update you will then have a record that will be locked for ever or until another client calls unlock before lock.

    This will probably work but it�s not nice:

    db.lock(recordNumber);
    try {
    db.update(recordNumber, ...);
    }
    finally {
    db.unlock(recordNumber);
    }


    As i said before the locking is not at all needed if all access to the database is thread safe.
     
    Andrew Monkhouse
    author and jackaroo
    Posts: 12200
    280
    Mac IntelliJ IDE Firefox Browser Oracle C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Anton,

    then, to determine if it is ok to call those methods, I would call for locking:



    Two questions:
  • Why are you calling notifyAll() in your locking method? (Who are you notifying, and what are you notifying them about?)
  • Are you sure that you are handling InterruptedException correctly? (What can cause this exception to be thrown? What state will your program be in then? What should you do from there?)



  • Regards, Andrew
     
    Andrew Monkhouse
    author and jackaroo
    Posts: 12200
    280
    Mac IntelliJ IDE Firefox Browser Oracle C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Mike,

    Originally posted by Mike Vess:
    I does not matter weather or not i have an unique object for each client that connects to the server. The problem is that the interface provided by sun not makes it possible to make sure that it is the same client that lockes the record that then updates or deletes it.

    public void lock(int recNo) throws RecordNotFoundException;
    public void unlock(int recNo) throws RecordNotFoundException;

    public void update(int recNo, String[] data) throws RecordNotFoundException;

    In the update method i can�t tell if it is the same client that have called the lock method and another question is what i should do in update if the record not is locked? Call lock method if it is not locked or throw RecordNotFoundException (which would be silly) or throw a RuntimeException (which also would be silly) or just ignore if the record is locked or not and just do the update.



    But if you had a unique instance of the Data object for each connected client, then you could use the instance of the Data object itself as the client identifier.

    That is, in the lock() code, you can store this in your map of who owns what lock, and in your update method, you can compare this with the owner of the lock - make sure they match.

    Regards, Andrew
     
    Ranch Hand
    Posts: 1033
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Andrew Monkhouse:
    Hi Mike,



    But if you had a unique instance of the Data object for each connected client, then you could use the instance of the Data object itself as the client identifier.

    That is, in the lock() code, you can store this in your map of who owns what lock, and in your update method, you can compare this with the owner of the lock - make sure they match.

    Regards, Andrew



    This sounds like a very good way to proceed, I'm assuming that the clients have a way of uniquely identifying themselves, such as a client id obtained the first time they issue a command over the network and used afterwards to get the Data instance from a Map.

    On the first command the client could provide clientID=-1 and the network code would create a new clientID and register it in the Map with its Data instance. On later commands the client would use the provided id to recover thier Data instance.

    Andrew, is this what you meant, or is there a better way to identify the client?
     
    Anton Golovin
    Ranch Hand
    Posts: 531
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Mike Vess:


    As i said before the locking is not at all needed if all access to the database is thread safe.



    I completely agree with you. I do think, though, that the assignment is pushing us to cache the records because then locking would be necessary. I was originally going to implement direct io access, but then, if I lock the datafile, I did not need locking. The only solution was to use a cache.
     
    Anton Golovin
    Ranch Hand
    Posts: 531
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Andrew Monkhouse:
    Hi Anton,

    Two questions:

  • Why are you calling notifyAll() in your locking method? (Who are you notifying, and what are you notifying them about?)
  • Are you sure that you are handling InterruptedException correctly? (What can cause this exception to be thrown? What state will your program be in then? What should you do from there?)



  • Regards, Andrew



    I was thinking in this way: if the thread is going to go to wait, how are other threads going to know to grab control? Maybe I misunderstand something - threading is very difficult for me - but that is what I was thinking. For the second question, I was sending control to the top of the loop to keep checking if the record is unlocked.
     
    Mike Vess
    Ranch Hand
    Posts: 41
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Anton Golovin:
    I completely agree with you. I do think, though, that the assignment is pushing us to cache the records because then locking would be necessary. I was originally going to implement direct io access, but then, if I lock the datafile, I did not need locking. The only solution was to use a cache.



    Why is locking neccesary if the records are cached? If you cache the records you only have to make all access to the cache thread safe and no locking will be needed. Caching all records should never work in a production environment, think about the amount of memory that it will take to cache all records in a large database, i dont think this is the right way to do it.

    Exactly WHAT im i supposed to do if a client calls update before lock is called? In sun:s interface update method only RecordNotFound exception can be thrown. What i whould like to do here is to throw a NotLockedException or something but that is not possible.

    Andrew Monkhouse:
    But if you had a unique instance of the Data object for each connected client, then you could use the instance of the Data object itself as the client identifier.



    I agree with you there but the implementation would not be nice. Here you would have to have a static HashMap of locked records and a static File instance for database access and another drawback with this solution is that you won�t be able to use the dataclass to connect to separate databases in the same jvm because of the static variables.

    Anton Golovin:
    I was thinking in this way: if the thread is going to go to wait, how are other threads going to know to grab control? Maybe I misunderstand something.



    You should call notifyAll in unlock method to notify threads waiting to lock a record in lock method. Calling notifyAll in lock method makes no sense at all.
     
    Anton Golovin
    Ranch Hand
    Posts: 531
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Locking is necessary because client A and client Z may both be going for record Q, however, the record Q may be deleted, rewritten in the time they get to it. It's like a race condition. Locking prevents that. In terms of how poorly the interface is designed, it is true, it's designed poorly, especially if it is missing the cookie value.
     
    Mike Vess
    Ranch Hand
    Posts: 41
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Locking is necessary because client A and client Z may both be going for record Q, however, the record Q may be deleted, rewritten in the time they get to it.



    This is not true. If all access to the database is thread safe you will never ever get a record that have been deleted or updated at the same time. But ofcourse the record can be removed just before the client calls read but that can also happen i a client calls lock on a record that just have been removed.
     
    Andrew Monkhouse
    author and jackaroo
    Posts: 12200
    280
    Mac IntelliJ IDE Firefox Browser Oracle C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Peter,

    Originally posted by peter wooster:
    This sounds like a very good way to proceed, I'm assuming that the clients have a way of uniquely identifying themselves, such as a client id obtained the first time they issue a command over the network and used afterwards to get the Data instance from a Map.

    On the first command the client could provide clientID=-1 and the network code would create a new clientID and register it in the Map with its Data instance. On later commands the client would use the provided id to recover thier Data instance.

    Andrew, is this what you meant, or is there a better way to identify the client?[/QB]



    That is an easy way of doing things. Another alternative is by Applying the Factory Pattern to RMI.

    If you are an EJB Session Bean programmer, you should already be familiar with this basic concept: the Home interface is basically a factory - you use that to create() the real EJB - of which there is one per client. Obviously, if there is one Session Bean per client, then it can have it's own private instance of whatever it needs as well.

    Back in the old Fly By Night Services assignment, this was the most common way of solving the problem we were given: we did not have cookies, and the client had to call the add(), modify(), delete(), and read() methods.

    Regards, Andrew
     
    Andrew Monkhouse
    author and jackaroo
    Posts: 12200
    280
    Mac IntelliJ IDE Firefox Browser Oracle C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Mike,

    Originally posted by Mike Vess:

    This will probably work but it�s not nice:


    As i said before the locking is not at all needed if all access to the database is thread safe.



    Ooops - missed this one before.

    The logical record locking and having thread safe access to the data file serve two totally different purposes.

    You are right in that you need thread safe access to the data file to ensure that only one thread is accessing the file at any given time.

    But booking a record has a totally separate issue: ensuring that only one client books the record. This requires your booking process to check that the record is still available to be booked, and then book it. Since there are two steps involved here, you need some way of making sure that no other thread can book your record in between those two steps.

    To make this a bit clearer, we are talking about two threads (A and B) both trying to book the same record:

  • Thread A checks that the record is still available
  • Thread B checks that the record is still available
  • Thread A updates the record
  • Thread B updates the record



  • Now you could avoid that by putting the booking code inside a synchronized block. However:
  • This reduces concurrent access to your database, as only one client at a time can be doing bookings
  • This assumes your booking method is in the database server logic. If your booking method is in the client software, you cannot do this. (This is not an argument for where the booking logic should be though - see the long thread "Should lock methods be callable by the client" for arguments about where the booking method should be ).

  • The better way of handling this is to use logical record locking around your booking code. Then as long as one client owns the lock, it knows that no other thread can lock it.

    Logical record locking can help you avoid the problem I originally mentioned (that of two clients both thinking that the record is unmodified and both updating it) - can you see how? (I don't want to just give the answer away).

    Regards, Andrew
     
    Andrew Monkhouse
    author and jackaroo
    Posts: 12200
    280
    Mac IntelliJ IDE Firefox Browser Oracle C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Anton

    Originally posted by Andrew Monkhouse:
    Why are you calling notifyAll() in your locking method? (Who are you notifying, and what are you notifying them about?)

    Originally posted by Anton Golovin:
    I was thinking in this way: if the thread is going to go to wait, how are other threads going to know to grab control? Maybe I misunderstand something - threading is very difficult for me - but that is what I was thinking.



    This is a common mistake - when two threads both try to access code within a synchronized block, one will get access, and the other will go into blocked state. Not wait state. The only way to enter wait state is by explictly calling one of the wait() methods.

    When you call notify() or notifyAll() you are only notifying threads that are in wait state that they should wake up and try to reaquire the monitor (or mutex (Mutual Exclusion) lock) for the object you are synchronizing on (which they will fail to do since your thread is still in the synchronized block - therefore after waking, they will effectively go to blocked state.

    Once your thread releases the monitor on the synchronized object, the JVM will pick one of the threads that is blocked for that monitor, and allow it to continue processing. All other threads will remain in the blocked state.

    Going into wait state automatically releases the monitor on the synchronized object.

    Lets try and do this as a concrete example. Three threads all try to gain a lock on record # 1 at the same time:

  • Thread A gets the monitor for the synchronized block's object (this)
  • Thread B cannot get the monitor - enters blocked state
  • Thread C cannot get the monitor - enters blocked state
  • Thread A locks record 1, then leaves the synchronized block - thread B is no longer blocked
  • Thread B gets the monitor
  • Thread C remains blocked
  • Thread B tries to lock the record, calls notifyAll() (nobody waiting, so no effect), and goes into wait state (releasing the monitor)
  • Thread C gets the monitor
  • Thread C tries to lock the record, calls notifyAll() (waking thread B which goes back into blocked state), goes into wait state (releasing the monitor)
  • Thread B gets the monitor
  • Thread B tries to lock the record, calls notifyAll() (waking thread B which goes back into blocked state), goes into wait state (releasing the monitor)
  • ...


  • Because of that call to notifyAll(), the two threads that are waiting will keep waking each other up forever - consuming a lot of CPU cycles in the meantime.

    Without that call to notifyAll(), both threads B and C will go into wait state, and consume no CPU cycles until the unlock() method calls notifyAll() when a record is unlocked.

    As with everything - what I wrote may make perfect sense, or it may not make any sense at all. If the later is true, then you might want to look at the J2SE API documentation for the Object class, specifically the wait(), notify() and notifyAll() methods. Or look at any other book detailing threading.

    Originally posted by Andrew Monkhouse:
    Are you sure that you are handling InterruptedException correctly? (What can cause this exception to be thrown? What state will your program be in then? What should you do from there?)

    Originally posted by Anton Golovin:
    For the second question, I was sending control to the top of the loop to keep checking if the record is unlocked.



    I can see that you will be going back to the top of the loop. The question here is - who is throwing this InterruptedException, and why are they doing it? If you are throwing that Exception, then you may be right in resuming the loop. But if you are not throwing it, then who has thrown it? If you don't know who is throwing it, then can you be sure that you are safe to just resume the loop?

    Regards, Andrew

     
    Andrew Monkhouse
    author and jackaroo
    Posts: 12200
    280
    Mac IntelliJ IDE Firefox Browser Oracle C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Mike,



    Originally posted by Andrew Monkhouse:
    But if you had a unique instance of the Data object for each connected client, then you could use the instance of the Data object itself as the client identifier.

    Originally posted by Mike Vess:
    I agree with you there but the implementation would not be nice. Here you would have to have a static HashMap of locked records and a static File instance for database access and another drawback with this solution is that you won�t be able to use the dataclass to connect to separate databases in the same jvm because of the static variables.



    You can get around those issues by moving the static information into their own classes. For example, a common solution mentioned in this forum is having a separate LockManager class which just handles the locking for a given database. You can define your LockManager to be a Multiton (a controlled number of instances) - then you can have a different instance of the LockManager depending on (for example) the fully qualified database file name. As long as two instances of the Data class try to get an instance of the LockManager for the same data file, they will end up with the same instance of the LockManager. Same applies for the classes for accessing the physical data file. Your Data class then becomes a Facade to the classes which do the real work.

    You may feel that this may not be the most efficient / most elegant / most ... solution, but what is more important is whether you think the alternative is a viable solution given the requirements. I believe the instructions are clear that the Data class needs to ensure that only the client who locked a record can update it - I don't know if you feel you can ignore that requirement.

    Regards, Andrew
     
    peter wooster
    Ranch Hand
    Posts: 1033
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Andrew Monkhouse:
    Hi Peter,



    That is an easy way of doing things. Another alternative is by Applying the Factory Pattern to RMI.

    If you are an EJB Session Bean programmer, you should already be familiar with this basic concept: the Home interface is basically a factory - you use that to create() the real EJB - of which there is one per client. Obviously, if there is one Session Bean per client, then it can have it's own private instance of whatever it needs as well.

    Back in the old Fly By Night Services assignment, this was the most common way of solving the problem we were given: we did not have cookies, and the client had to call the add(), modify(), delete(), and read() methods.

    Regards, Andrew



    Thank you, that is quite similar to what I'm doing, except that I'm using plain sockets not RMI. My first command after connection is a request to a SessionManager that builds and caches the Data instance and returns a session number that is then used in other commands. The SessionManager acts a Data factory.

    My commands follow the GoF Command pattern with the modification that they know how to find their target once they cross the network by using the Singleton SessionManager and the sessionID to get the target Data instance.
     
    Anton Golovin
    Ranch Hand
    Posts: 531
    1
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi, Andrew. It makes perfect sense. If they block, and are automatically resumed by the JVM, then my notifyAll call will wake up threads waiting on the logical lock to be unlocked. However, since the thread calling notifyAll has not locked the record, it should not be its place to notify other threads - it should be the place of the thread which unlocks the record. I understand. I did not konw, or missed, that threads waiting to acquire the lock on an object block, as opposed to wait. It was a logical failure on my part: waiting threads must have owned the lock and now release it; threads in question never owned the lock in the first place.

    Thanks for the clarification.
     
    Ranch Hand
    Posts: 783
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    The logical record locking and having thread safe access to the data file serve two totally different purposes.

    Thanks for clearing that up Andrew. I generally understood the locking process, but your explanation made it much more understandable for me.
    reply
      Bookmark Topic Watch Topic
    • New Topic