管理多个锁

时间:2014-01-22 15:18:47

标签: java concurrency

我有以下情况:我正在同时处理具有给定密钥的请求。只要正在进行的每个密钥都是唯一的,我就可以同时处理任意数量的请求。

我是Java中的并发新手。必须有一些模式/实用/现有问题,但我无法弄清楚要搜索什么。希望有人能指出我正确的方向,或评论我到目前为止所做的事情。

此类管理锁:

class LockMap<K> {
    private Map<K, Object> locks = new HashMap<>();

    void acquireLock(K key) throws InterruptedException {
        Object lockObj;
        synchronized (locks) {
            lockObj = locks.get(key);

            if (lockObj == null) lockObj = new Object();

            locks.put(key, lockObj);
        }

        synchronized (lockObj) {
            lockObj.wait();
        }
    }

    void releaseLock(K key) {
        Object lockObj;
        synchronized (locks) {
            lockObj = locks.get(key);
            locks.remove(key);
        }

        if (lockObj != null) {
            synchronized (lockObj) {
                lockObj.notify();
            }
        }
    }
}

然后我像这样使用锁管理器:

// lockMap is instance of LockMap shared across all threads
void doSomething(K key) {
    lockMap.acquireLock(key);
    try {
        // something
    } finally {
        lockMap.releaseLock(key);
    }
}

这是正确的方法吗?

2 个答案:

答案 0 :(得分:3)

以下解决方案不会锁定LockMap,因此极端并行。它使用定制的Locks来跟踪可以删除它们的时刻,并处理并发删除/创建。

class Lock {
    boolean busy=true; // locked state, a thread is working
    int waitCount=0; // number of waiting threads

        /** returns true if lock succeeded */
    synchronized boolean tryLock() throws InterruptedException {
        if (busy) {
            waitCount++; 
        } else if (waitCount==0){
                // such values mean that the lock is deleted
            return false;
        } else {
            busy=true;
            return true;
        }
        for (;;) {
            wait();
            if (!busy) {
                waitCount--;
                busy=true;
                return true;
            }
        }
    }

}

class LockMap<K> {
    private ConcurrentHashMap<K, Lock> locks = new ConcurrentHashMap<>();

    void acquireLock(K key) throws InterruptedException {
        for (;;) {
            Lock lockObj = locks.get(key);
            if (lockObj==null) {
                Lock myLockObj = new Lock();
                lockObj=locks.putIfAbsent(key, myLockObj);
                if (lockObj==null) {
                    // successfully inserted, and so locked
                    return;
                }
            }
            // lockObj existed, lock it or wait in queue
            if (lockObj.tryLock()) {
                return;
            }
        }
    }

    void releaseLock(K key) {
        Lock lockObj = locks.get(key);
        synchronized (lockObj) {
            lockObj.busy=false;
            if (lockObj.waitCount==0) {
                locks.remove(key);
            } else {
                lockObj.notify();
            }
        }
    }
}

答案 1 :(得分:2)

这个怎么样:

创建ConcurrentHashMap<K,Semaphore>

ConcurrentMap<K, Semaphore> myMap = new ConcurrentHashMap<>();

在您的doSomething()方法中,使用映射的putIfAbsent()方法向地图添加一个允许的信号量,只有在地图中不存在该键时才会显示。

随后在密钥上执行get()以获取该密钥的信号量,然后执行您的操作。完成后释放信号量。

void doSomething(K key) {
     myMap.putIfAbsent(key, new Semaphore(1));
     Semaphore s = myMap.get(myKey);
     s.aquire();
     try {
     // do stuff
     } finally {
       s.release();
     }
 }

这个方案唯一真正的问题是如果你的密钥列表会无限增长,我没有一个良好的无竞争条件的策略来从地图中删除信号量。 (但是如果你知道你会一遍又一遍地重复使用相同的密钥,或者列表会慢慢增长,那么也许这没关系。)