Here is the source for the single layered lock.
This is what I hacked through, getDatabaseLock need to get the
curDatabaseLock object, while getLock synchronized based on
this. I use "this" object to guard the access to the hashMap locks and the rest variables.
It is not as nice as the two level locks presented above.
Thx.
B.T.W, I changed the lock signature on the server side, where
on client, it is still the old lock signature.
-----------------------------------------------------
package com.hachi.server.lock;
import com.hachi.server.connect.*;
import java.util.*;
public class LockManager {
public static int DATABASE_LOCK_RECORD = -1;
int waitForDatabaseLock = 0;
RemoteConnection curDatabaseLock = null;
HashMap locks;
ArrayList waitingForDatabaseLock = new ArrayList();
public LockManager() {
locks = new HashMap(); // key record number, value client sessionID
}
void getDatabaseLock(RemoteConnection sessionID) throws InterruptedException {
synchronized (this) {
waitForDatabaseLock++;
if (locks.isEmpty() && (curDatabaseLock ==null)) {
waitForDatabaseLock--;
curDatabaseLock = sessionID;
return;
}
waitingForDatabaseLock.add((RemoteConnectionImpl) sessionID);
}
synchronized (curDatabaseLock) {
while ( !sessionID.equals(curDatabaseLock)) {
wait();
}
}
synchronized (this) {
waitForDatabaseLock--;
int i = waitingForDatabaseLock.indexOf(sessionID);
waitingForDatabaseLock.remove(i);
}
}
void getLock(int recNum, RemoteConnection sessionID) throws InterruptedException {
Integer rec = new Integer(recNum);
if (recNum == DATABASE_LOCK_RECORD) {
getDatabaseLock(sessionID);
return;
}
synchronized (this) {
if ((curDatabaseLock ==null) && (locks.get(rec)==null)) {
locks.put(rec, (RemoteConnectionImpl) sessionID);
return;
}
while ( curDatabaseLock !=null | | locks.get(rec)!=null) {
wait(); //database lock has priority over row lock, so there must be no database lock.
}
locks.put(rec, (RemoteConnectionImpl) sessionID);
}
}
synchronized public void unLock(int recNum, RemoteConnection sessionID) {
// release DatabaseLock
if (curDatabaseLock !=null ) {//only database lock held, no row locked, only need to release database lock
if ((recNum != DATABASE_LOCK_RECORD) | | (!curDatabaseLock.equals(sessionID)) )
return;
if (waitForDatabaseLock > 0) {
curDatabaseLock = (RemoteConnection) waitingForDatabaseLock.get(0); // ANY_SESSION_LOCK is used to block the row lock.
synchronized (curDatabaseLock) {
// notify database lock
curDatabaseLock.notifyAll();
}
return;
}
else {
curDatabaseLock = null;
// notify row lock()
notifyAll();
}
}
// release row lock;
if (recNum == DATABASE_LOCK_RECORD) return;
Integer rec = new Integer(recNum);
RemoteConnection lockSession = (RemoteConnection) locks.get(rec);
if (lockSession.equals(sessionID)) {
locks.remove(rec);
if (locks.isEmpty() && waitForDatabaseLock > 0) {
RemoteConnection databaseSession = (RemoteConnection) waitingForDatabaseLock.get(0);
curDatabaseLock = databaseSession;
synchronized (curDatabaseLock) {
curDatabaseLock.notifyAll();
}
}
else {
curDatabaseLock = null;
notifyAll();
}
}
}
}