/*********************************************************************************** * @author John Miller * @version 1.0 * @date Thu Aug 21 17:19:52 EDT 2014 */ package transj; import java.util.concurrent.Semaphore; import java.util.HashMap; import java.util.Map; import static java.lang.System.out; /*********************************************************************************** * This class implements a lock table as a hash map. The object identifier (oid) * is used as the key to find the lock in the hash table. If the lock is not found, * the data object is not locked. This class can be used in the implementation * of locking protocols, such as Two-Phase Locking (2PL). * Caveat: shared/read locks are currently not implemented. */ public class LockTable { /******************************************************************************* * This class is used to represent an individual lock. * @param tid the id of the transaction holding the lock * @param shared whether the lock is shared (true) or exclusive (false) */ public class Lock { final int tid; final boolean shared; final Semaphore sem = new Semaphore (0); public Lock (int tid_, boolean shared_) { tid = tid_; shared = shared_; } // constructor } // Lock class /** associative map of locks held by transactions of the form (key = oid, value = lock) */ private final Map locks = new HashMap <> (); /******************************************************************************* * Acquire a shared/read lock on data object oid. * @param tid the transaction id * @param oid the data object id */ public void rl (int tid, int oid) { synchronized (this) { // not yet implemented } // synchronized } // rl /******************************************************************************* * Acquire an exclusive/write lock on data object oid. * @param tid the transaction id * @param oid the data object id */ public void wl (int tid, int oid) { Lock lock = null; boolean wait = false; synchronized (this) { lock = locks.get (oid); // find the lock if (lock != null) wait = true; } // synchronized if (wait) { try { lock.sem.acquire (); // wait for the lock to be released } catch (InterruptedException ex) { } // try } // if synchronized (this) { locks.put (oid, new Lock (tid, false)); // establish the lock in the table } // synchronized } // wl /******************************************************************************* * Unlock/release the lock on data object oid. * @param tid the transaction id * @param oid the data object id */ public void ul (int tid, int oid) { Lock lock = null; boolean error = false; synchronized (this) { lock = locks.get (oid); // find the lock if (lock == null) { error = true; // lock not found } else if (lock.tid == tid) { locks.remove (oid); // remove the lock from table lock.sem.release (); // signal waiting transactions } else { error = true; // lock not held/owned by tid lock.sem.release (); // signal waiting transactions } // if } // synchronized if (error) out.println ("Error: ul: no lock for oid = " + oid + " found/owned"); } // ul /******************************************************************************* * Convert the lock table to a string. */ public String toString () { synchronized (this) { return "LockTable ( " + locks + " )"; } // synchronized } // toString /*********************************************************************************** * Test the LockTable class. Note, typically an object of this class would would * be accessed by multiple threads. */ public static void main (String [] args) { LockTable ltab = new LockTable (); ltab.wl (0, 0); ltab.wl (1, 1); ltab.wl (2, 2); out.println ("ltab = " + ltab); ltab.ul (1, 1); // transaction 1 unlocks object 1 ltab.ul (0, 0); // transaction 0 unlocks object 0, try commenting out out.println ("ltab = " + ltab); ltab.wl (1, 0); // transaction 1 tries to get lock on object 0 out.println ("ltab = " + ltab); } // main } // LockTable class