Locking Mechanism


Most RDBMSs, especially commercial ones, provide a built-in locking mechanism. However, this section explains creating your own generic database-locking mechanism that prevents two clients from trying to change the same record simultaneously . The programmatic locking discussed here is required only for the rudimentary database used in the certification assignment.

A locking mechanism is needed for a database because you need to remove the possibility of two users simultaneously trying to update the same record ”also called colliding . Collisions can corrupt database records, so you need to add the capability of allowing database records to be locked while changes are pending. One client locking a record prevents other clients from locking or modifying the same record. Although the approach presented here is not the transaction control found in a true DBMS, it does meet the fundamental requirement of preventing updates by more than one user at the same time. Adding record locking helps control simultaneous access to the same records. Choosing the right locking method for your application is important. If you're not sure how you want to support record locking, you can use the locking manager class documented later in this section.

For my solution, I implemented a separate LockManager class that allows Insert Row Locking (IRL), so that if two clients attempt to perform the "lock, read/modify/write, unlock" sequence concurrently, both modification attempts are handled sequentially. The LockManager class provides consistent and concurrent access to data in a multiuser environment (such as with RMI). Because IRL is necessary only in remote mode, not in local mode, implementing it as a separate LockManager class makes more sense. Locks prevent simultaneous updates of the same row of data. With a separate lock manager, only one user at a time can update a row of data. So while one user is reserving a seat for the Super Bowl, another user can't do the same thing. Without a good lock manager, users could overwrite each other's seat reservations , giving the impression that a seat could be sold to more than one person.

A row is locked by a given client, so the lock is associated with that client (for example, track the client with a clientId ). You can define this ID many ways. For example, you can use a static counter variable giving each new client a new and unique ID. Probably the easiest way to get an ID is to pass a unique reference to the client.

In the LockManager class shown in this section, the clientId variable is referred to with a WeakReference . Objects that have weak references are those that are weakly reachable , meaning that an object is garbage-collected if it doesn't have any strong references (that is, has only weak references) and the weak reference is set to NULL . For these objects, the garbage collector automatically clears all weak references to them. Then these objects are no longer referenced and can be garbage-collected when they are removed from memory. Therefore, when a lock is related to a weakly referred client, if the client dies, the lock does also, although the timing is not guaranteed . That way, should another client try to lock a record previously locked by a client who has died, the LockManager class removes the lock because the garbage collector has nullified that reference. The responsibility for record locking is kept in LockManager ; the responsibility for references to dead objects stays with the Java Virtual Machine (JVM), a clean separation of responsibilities.

Listing 7.1 shows an example of a locking mechanism.

Listing 7.1 An Example of a Locking Mechanism
 package superbowl.database; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.lang.ref.WeakReference; /**  * This class provides the basic Insert Row Locking (IRL),  * so that if two clients attempt to perform the sequence  * "lock, read/modify/write, unlock" concurrently, both  * modification attempts are handled sequentially. The LockManager  * provides consistent and concurrent access to data in a multiuser  * environment for a (e.g., RMI) database service.  *  * @author     Alain Trottier  * @version 1.0  10-Feb-2003  */ public class LockManager {     private final Map lockedRecords = new HashMap();     public final int DATABASE_LOCK = -1;     private WeakReference clientId, oldClientId;     /**      * Locks a given record by using the record number as key and connection      * object passed to it as value. A record number of -1 locks the entire      * database. Checks if another client locks a given record by calling      * isLockedByAnotherClient(record, client). It waits until the lock      * is removed. If another client locked this record but has died,      * the lock is released because the clientId is a WeakReference, which      * is garbage collected when the reference disappears. This way, there      * is no need for a fancy timeout or polling mechanism.      *      * @param recNum ID of the record for which a lock is requested.      * @param client client that wants to perform the locking.      */     synchronized public void lock(int recNum, Object client)                              throws InterruptedException     {         Integer record = new Integer(recNum);         Integer lockDB = new Integer(DATABASE_LOCK);         //if client dies without cleanup,         //you want to automatically ignore its locks         clientId = new WeakReference(client);         try         {                 if ( recNum == DATABASE_LOCK )                 {                     while (lockedRecords.containsKey(lockDB))                     {                         // must wait for database lock to release,                         // regardless of client                         wait();                     }                 } else                 {                     while (lockedRecords.containsKey(record)                           && !isLockedByAnotherClient(record, clientId))                     {                         wait();                     }                 }                 lockedRecords.put(record, clientId);         } catch (InterruptedException e)         {                 throw new InterruptedException ("Interrupted!", e);         }     }     /**      * Checks if a given record is locked by another client.      * If another client locked this record but has died,      * the lock is released because the clientId is a WeakReference, which      * is garbage collected when the reference disappears. This way, there      * is no need for a fancy timeout or polling mechanism.      *      * @param record ID of the record for which a lock is requested.      * @param client client that wants to perform the locking.      * @return is record locked or not.      */     synchronized public boolean isLockedByAnotherClient(Integer record,                                                         Object client)     {         oldClientId = (WeakReference)lockedRecords.get(record);         clientId = new WeakReference(client);         boolean isLocked = false;         if (oldClientId == null)         {                 // Value has been garbage-collected: client has died, so                 // remove unreferenced lock.                 lockedRecords.remove(record);         }else if (oldClientId != clientId)         {             // locked by another client             isLocked = true;         }         return isLocked;     }     /**      * Unlocks a given record by using the record number      * as key and the client passed to it as value.      *      * @param recNum ID of the record for which a lock is requested.      * @param client client that wants to perform the locking.      */     synchronized public void unlock(int recNum, Object client)     {         Integer record = new Integer(recNum);         clientId = new WeakReference(client);         if (lockedRecords.get(record).equals(clientId))         {             lockedRecords.remove(record);             notifyAll();         }     }     /**      * Clears all pending locks for a particular client.      *      * @param client client that has previously locked records.      */     synchronized public void clearLocks(Object client)     {         clientId = new WeakReference(client);         Iterator iter = lockedRecords.entrySet().iterator();         for (Iterator i = lockedRecords.values().iterator(); i.hasNext(); )         {             if (clientId.equals(i.next()))             {                  i.remove();             }         }     }     /**      * Clears all pending locks regardless of client.      */     synchronized public void clearLocks()     {         lockedRecords.clear();     } } 

An especially strong aspect of this lock manager is that it's abstract enough to work on just about any database system you can invent for the assignment. The core principle is mapping the record number as the key and a reference to the client as the value. With that mapping, you can easily check whether someone has a lock on a record by simply searching for the key. If the key is there, then someone has the lock. This part is easy, but what happens if the lock owner disappears? This question has stumped many candidates. Some use a timeout mechanism by periodically checking whether the lock owner is still valid, or some simply remove a lock that has aged past a certain threshold or time limit.

Although there are many ways to handle dead lock owners , my approach was to place the responsibility for tracking when clients die with the JVM. When the client dies, the reference is scheduled for garbage collection by using a WeakReference object. The client requests a lock on a record by passing a reference of itself, as a WeakReference object, along with the record number. If the client dies, this WeakReference object is garbage collected. You can check whether this happened by testing if clientId is null. There are many ways to assign client IDs, but I thought using the WeakReferenced clientId was a valid approach to this portion of the project.



JavaT 2 Developer Exam CramT 2 (Exam CX-310-252A and CX-310-027)
JavaT 2 Developer Exam CramT 2 (Exam CX-310-252A and CX-310-027)
ISBN: N/A
EAN: N/A
Year: 2003
Pages: 187

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net