Java 设计基于密钥的锁(或锁映射)

Java 设计基于密钥的锁(或锁映射),java,concurrency,locking,Java,Concurrency,Locking,我正试图设计一个基于密钥的锁定工具:类似于普通的可重入锁,但不是lock()和unlock(),而是锁定(key)和解锁(key),如果key.equals(key1),没有人能够同时锁定(key1) 这个代码行吗?有更有效的解决方案吗?我特别不喜欢while循环,因为它试图在地图中设置锁 package luca; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap;

我正试图设计一个基于密钥的锁定工具:类似于普通的可重入锁,但不是lock()和unlock(),而是锁定(key)和解锁(key),如果key.equals(key1),没有人能够同时锁定(key1)

这个代码行吗?有更有效的解决方案吗?我特别不喜欢while循环,因为它试图在地图中设置锁

package luca;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.ReentrantLock;

public class KeyedReentrantLock<K> {
    private ConcurrentMap<K, ReentrantLock> lockMap = new ConcurrentHashMap<K, ReentrantLock>();

    public void lock(K key) {
        ReentrantLock oldLock = lockMap.get(key);
        if (oldLock != null && oldLock.isHeldByCurrentThread()){
            // increase lock count and return.
            oldLock.lock();
            return;
        }
        ReentrantLock newLock = new ReentrantLock();
        newLock.lock();
        while ((oldLock = lockMap.putIfAbsent(key, newLock)) != null){
            // wait for the old lock to be released;
            oldLock.lock();
            oldLock.unlock();
        }
        return;
    }

    public void unlock(K key){
        ReentrantLock lock = lockMap.get(key);
        if (lock == null) throw new IllegalMonitorStateException("There was no lock for this key!");
        if (lock.getHoldCount() == 1){
            lockMap.remove(key);
        }
        lock.unlock();
    }

}
package-luca;
导入java.util.concurrent.ConcurrentHashMap;
导入java.util.concurrent.ConcurrentMap;
导入java.util.concurrent.locks.ReentrantLock;
公共类KeyedReentrantLock{
私有ConcurrentMap lockMap=新ConcurrentHashMap();
公共无效锁(K钥匙){
ReentrantLock oldLock=lockMap.get(key);
if(oldLock!=null&&oldLock.isHeldByCurrentThread()){
//增加锁计数并返回。
lock();
返回;
}
ReentrantLock newLock=新的ReentrantLock();
newLock.lock();
while((oldLock=lockMap.putIfAbsent(key,newLock))!=null){
//等待旧锁释放;
lock();
oldLock.unlock();
}
返回;
}
公共无效解锁(K密钥){
ReentrantLock lock=lockMap.get(key);
如果(lock==null)抛出新的IllegalMonitorStateException(“此密钥没有锁!”);
if(lock.getHoldCount()==1){
锁图。移除(钥匙);
}
lock.unlock();
}
}

为什么不使用简单的条带锁定,比如:

/**
 * Striped locks holder, contains array of {@link java.util.concurrent.locks.ReentrantLock}, on which lock/unlock
 * operations are performed. Purpose of this is to decrease lock contention.
 * <p>When client requests lock, it gives an integer argument, from which target lock is derived as follows:
 * index of lock in array equals to <code>id & (locks.length - 1)</code>.
 * Since <code>locks.length</code> is the power of 2, <code>locks.length - 1</code> is string of '1' bits,
 * and this means that all lower bits of argument are taken into account.
 * <p>Number of locks it can hold is bounded: it can be from set {2, 4, 8, 16, 32, 64}.
  */
public class StripedLock {
    private final ReentrantLock[] locks;

    /**
     * Default ctor, creates 16 locks
     */
    public StripedLock() {
        this(4);
    }

    /**
     * Creates array of locks, size of array may be any from set {2, 4, 8, 16, 32, 64} 
     * @param storagePower size of array will be equal to <code>Math.pow(2, storagePower)</code>
     */
    public StripedLock(int storagePower) {
        if (!(storagePower >= 1 && storagePower <= 6)) { throw new IllegalArgumentException("storage power must be in [1..6]"); }

        int lockSize = (int) Math.pow(2, storagePower);
        locks = new ReentrantLock[lockSize];
        for (int i = 0; i < locks.length; i++)
            locks[i] = new ReentrantLock();
    }

    /**
     * Locks lock associated with given id.
     * @param id value, from which lock is derived
     */
    public void lock(int id) {
        getLock(id).lock();
    }

    /**
     * Unlocks lock associated with given id.
     * @param id value, from which lock is derived 
     */
    public void unlock(int id) {
        getLock(id).unlock();
    }

    /**
     * Map function between integer and lock from locks array
     * @param id argument
     * @return lock which is result of function 
     */
    private ReentrantLock getLock(int id) {
        return locks[id & (locks.length - 1)];
    }
}

请参考下面的示例代码,我已经为每个线程创建了一个新锁

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;

public class Processor implements Runnable {

    final static ConcurrentHashMap<Integer, ReentrantReadWriteLock> CONCURRENT_HASH_MAP = new ConcurrentHashMap<Integer, ReentrantReadWriteLock>();

    final private Employee employee;

    public Processor(int id) {
        this.employee = new Employee(id);
    }

    public void run() {
        processDate(employee);
    }

    /**
     * Method to be shared
     * 
     * @param id
     */
    public void processDate(final Employee employee) {

        final int employeeId = employee.getId();
        ReentrantReadWriteLock monitoredObject = new ReentrantReadWriteLock();
        System.out.println("Before taking the lock"
                    + Thread.currentThread().getName());
        while (CONCURRENT_HASH_MAP.putIfAbsent(employeeId, monitoredObject) != null) {
        }
        ReadLock lock = monitoredObject.readLock();
        lock.lock();
        try {
            processXML(employee);
        } catch (Exception e) {
            e.printStackTrace();
        }
        CONCURRENT_HASH_MAP.remove(employeeId);
        lock.unlock();
    }

    /**
     * For similar id object this will run one by one but for different objects
     * this will run parallal.
     * 
     * This method will execute serially if called by multiple threads for
     * employee with same emp id
     * 
     * @throws Exception
     */
    public void processXML(final Employee employee) throws Exception {
        System.out.println("Process XML for " + employee.getId()
                + Thread.currentThread().getName());
        Thread.sleep(2000);
        System.out.println("Done XML Processing for " + employee.getId()
                + Thread.currentThread().getName());
        ReentrantReadWriteLock lock = CONCURRENT_HASH_MAP.get(employee.getId());
        System.out.println("lock object " + lock + "queue length "
                + lock.getQueueLength());
    }

    class Employee {
        private Integer id;

        public Employee(final int id) {
            this.id = id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public Integer getId() {
            return id;
        }
    }

    public static void main(String[] args) {
        final ExecutorService executorService = Executors.newFixedThreadPool(10);
        long startTime = System.currentTimeMillis();
        /**
         * In Processors Constructor instead of i write 1 and see the
         * difference.
         */
        for (int i = 1; i <= 5; i++)
            executorService.submit(new Processor(i));
        executorService.shutdown();
        /*
         * Let the main thread wait till the executor service is terminated to
         * observe the total time taken
         */
        while (executorService.isTerminated() != true) {
        }
        long endTime = System.currentTimeMillis();
        long timeTaken = endTime - startTime;
        System.out.println("time taken.... " + timeTaken + " ms");

    }
}
import java.util.concurrent.ConcurrentHashMap;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
导入java.util.concurrent.locks.ReentrantReadWriteLock;
导入java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
公共类处理器实现可运行{
最终静态ConcurrentHashMap CONCURRENT_HASH_MAP=新ConcurrentHashMap();
最终私人雇员;
公共处理器(int id){
this.employee=新员工(id);
}
公开募捐{
处理日期(员工);
}
/**
*要共享的方法
* 
*@param-id
*/
公共作废处理日期(最终员工){
final int employeeId=employee.getId();
ReentrantReadWriteLock monitoredObject=新建ReentrantReadWriteLock();
System.out.println(“取锁前”
+Thread.currentThread().getName());
while(CONCURRENT_HASH_MAP.putIfAbsent(employeeId,monitoredObject)!=null){
}
ReadLock lock=monitoredObject.ReadLock();
lock.lock();
试一试{
processXML(员工);
}捕获(例外e){
e、 printStackTrace();
}
并发散列映射移除(employeeId);
lock.unlock();
}
/**
*对于类似的id对象,这将逐个运行,但针对不同的对象
*这将运行视差。
* 
*如果被多个线程调用,此方法将串行执行
*具有相同emp id的员工
* 
*@抛出异常
*/
public void processXML(最终员工)引发异常{
System.out.println(“处理”+employee.getId()的XML)
+Thread.currentThread().getName());
《睡眠》(2000年);
System.out.println(“为”+employee.getId()完成XML处理)
+Thread.currentThread().getName());
ReentrantReadWriteLock=CONCURRENT_HASH_MAP.get(employee.getId());
System.out.println(“锁对象”+锁+“队列长度”
+lock.getQueueLength());
}
班级员工{
私有整数id;
公共雇员(最终内部id){
this.id=id;
}
公共无效集合id(整数id){
this.id=id;
}
公共整数getId(){
返回id;
}
}
公共静态void main(字符串[]args){
final ExecutorService ExecutorService=Executors.newFixedThreadPool(10);
long startTime=System.currentTimeMillis();
/**
*在Processors构造函数中,而不是我写1,并看到
*差别。
*/

对于(int i=1;i称之为
DynamicKeyLock。它是任何对象作为键的单个进程锁(
等于
+
hashcode`以获得唯一性)

Java 8的实现:

public class DynamicKeyLock<T> implements Lock
{
    private final static ConcurrentHashMap<Object, LockAndCounter> locksMap = new ConcurrentHashMap<>();

    private final T key;

    public DynamicKeyLock(T lockKey)
    {
        this.key = lockKey;
    }

    private static class LockAndCounter
    {
        private final Lock lock = new ReentrantLock();
        private final AtomicInteger counter = new AtomicInteger(0);
    }

    private LockAndCounter getLock()
    {
        return locksMap.compute(key, (key, lockAndCounterInner) ->
        {
            if (lockAndCounterInner == null) {
                lockAndCounterInner = new LockAndCounter();
            }
            lockAndCounterInner.counter.incrementAndGet();
            return lockAndCounterInner;
        });
    }

    private void cleanupLock(LockAndCounter lockAndCounterOuter)
    {
        if (lockAndCounterOuter.counter.decrementAndGet() == 0)
        {
            locksMap.compute(key, (key, lockAndCounterInner) ->
            {
                if (lockAndCounterInner == null || lockAndCounterInner.counter.get() == 0) {
                    return null;
                }
                return lockAndCounterInner;
            });
        }
    }

    @Override
    public void lock()
    {
        LockAndCounter lockAndCounter = getLock();

        lockAndCounter.lock.lock();
    }

    @Override
    public void unlock()
    {
        LockAndCounter lockAndCounter = locksMap.get(key);
        lockAndCounter.lock.unlock();

        cleanupLock(lockAndCounter);
    }


    @Override
    public void lockInterruptibly() throws InterruptedException
    {
        LockAndCounter lockAndCounter = getLock();

        try
        {
            lockAndCounter.lock.lockInterruptibly();
        }
        catch (InterruptedException e)
        {
            cleanupLock(lockAndCounter);
            throw e;
        }
    }

    @Override
    public boolean tryLock()
    {
        LockAndCounter lockAndCounter = getLock();

        boolean acquired = lockAndCounter.lock.tryLock();

        if (!acquired)
        {
            cleanupLock(lockAndCounter);
        }

        return acquired;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException
    {
        LockAndCounter lockAndCounter = getLock();

        boolean acquired;
        try
        {
            acquired = lockAndCounter.lock.tryLock(time, unit);
        }
        catch (InterruptedException e)
        {
            cleanupLock(lockAndCounter);
            throw e;
        }

        if (!acquired)
        {
            cleanupLock(lockAndCounter);
        }

        return acquired;
    }

    @Override
    public Condition newCondition()
    {
        LockAndCounter lockAndCounter = locksMap.get(key);

        return lockAndCounter.lock.newCondition();
    }
}
公共类DynamicKeyLock实现锁
{
私有最终静态ConcurrentHashMap locksMap=新ConcurrentHashMap();
私钥;
公共动态锁(T锁钥匙)
{
this.key=lockKey;
}
专用静态类锁和计数器
{
private final Lock=new ReentrantLock();
私有最终AtomicInteger计数器=新的AtomicInteger(0);
}
私有锁和计数器getLock()
{
返回锁映射计算(键,(键,锁和计数器内部)->
{
if(lockAndCounterInner==null){
lockAndCounterInner=新的LockAndCounter();
}
lockAndCounterInner.counter.incrementAndGet();
返回锁和计数器内部;
});
}
专用真空清洁锁(锁和柜锁和柜外)
{
if(lockAndCounterOuter.counter.decrementAndGet()==0)
{
锁映射计算(键,(键,锁和计数器内部)->
{
if(lockAndCounterInner==null | | lockAndCounterInner.counter.get()==0){
返回null;
}
返回锁和计数器内部;
});
}
}
@凌驾
公共无效锁()
{
LockAndCounter LockAndCounter=getLock();
lock和counter.lock.lock();
}
@凌驾
公开作废解锁()
{
锁和计数器锁和计数器
public class DynamicKeyLock<T> implements Lock
{
    private final static ConcurrentHashMap<Object, LockAndCounter> locksMap = new ConcurrentHashMap<Object, LockAndCounter>();
    private final T key;

    public DynamicKeyLock(T lockKey) {
        this.key = lockKey;
    }

    private static class LockAndCounter {
        private final Lock lock = new ReentrantLock();
        private final AtomicInteger counter = new AtomicInteger(0);
    }

    private LockAndCounter getLock()
    {
        while (true) // Try to init lock
        {
            LockAndCounter lockAndCounter = locksMap.get(key);

            if (lockAndCounter == null)
            {
                LockAndCounter newLock = new LockAndCounter();
                lockAndCounter = locksMap.putIfAbsent(key, newLock);

                if (lockAndCounter == null)
                {
                    lockAndCounter = newLock;
                }
            }

            lockAndCounter.counter.incrementAndGet();

            synchronized (lockAndCounter)
            {
                LockAndCounter lastLockAndCounter = locksMap.get(key);
                if (lockAndCounter == lastLockAndCounter)
                {
                    return lockAndCounter;
                }
                // else some other thread beat us to it, thus try again.
            }
        }
    }

    private void cleanupLock(LockAndCounter lockAndCounter)
    {
        if (lockAndCounter.counter.decrementAndGet() == 0)
        {
            synchronized (lockAndCounter)
            {
                if (lockAndCounter.counter.get() == 0)
                {
                    locksMap.remove(key);
                }
            }
        }
    }

    @Override
    public void lock()
    {
        LockAndCounter lockAndCounter = getLock();

        lockAndCounter.lock.lock();
    }

    @Override
    public void unlock()
    {
        LockAndCounter lockAndCounter = locksMap.get(key);
        lockAndCounter.lock.unlock();

        cleanupLock(lockAndCounter);
    }


    @Override
    public void lockInterruptibly() throws InterruptedException
    {
        LockAndCounter lockAndCounter = getLock();

        try
        {
            lockAndCounter.lock.lockInterruptibly();
        }
        catch (InterruptedException e)
        {
            cleanupLock(lockAndCounter);
            throw e;
        }
    }

    @Override
    public boolean tryLock()
    {
        LockAndCounter lockAndCounter = getLock();

        boolean acquired = lockAndCounter.lock.tryLock();

        if (!acquired)
        {
            cleanupLock(lockAndCounter);
        }

        return acquired;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException
    {
        LockAndCounter lockAndCounter = getLock();

        boolean acquired;
        try
        {
            acquired = lockAndCounter.lock.tryLock(time, unit);
        }
        catch (InterruptedException e)
        {
            cleanupLock(lockAndCounter);
            throw e;
        }

        if (!acquired)
        {
            cleanupLock(lockAndCounter);
        }

        return acquired;
    }

    @Override
    public Condition newCondition()
    {
        LockAndCounter lockAndCounter = locksMap.get(key);

        return lockAndCounter.lock.newCondition();
    }
}
public class ProcessLockTest
{
    @Test
    public void testDifferentKeysDontLock() throws InterruptedException
    {
        ProcessLock<Object> lock = new ProcessLock<>(new Object());
        lock.lock();
        AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
        try
        {
            new Thread(() ->
            {
                ProcessLock<Object> anotherLock = new ProcessLock<>(new Object());
                anotherLock.lock();
                try
                {
                    anotherThreadWasExecuted.set(true);
                }
                finally
                {
                    anotherLock.unlock();
                }
            }).start();
            Thread.sleep(100);
        }
        finally
        {
            Assert.assertTrue(anotherThreadWasExecuted.get());
            lock.unlock();
        }
    }

    @Test
    public void testSameKeysLock() throws InterruptedException
    {
        Object key = new Object();
        ProcessLock<Object> lock = new ProcessLock<>(key);
        lock.lock();
        AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
        try
        {
            new Thread(() ->
            {
                ProcessLock<Object> anotherLock = new ProcessLock<>(key);
                anotherLock.lock();
                try
                {
                    anotherThreadWasExecuted.set(true);
                }
                finally
                {
                    anotherLock.unlock();
                }
            }).start();
            Thread.sleep(100);
        }
        finally
        {
            Assert.assertFalse(anotherThreadWasExecuted.get());
            lock.unlock();
        }
    }
}
public class KeyLock<K> {

    private /*static*/ final Set<K> lockedKeys = new HashSet<>();

    public /*static*/ void lock(K key) throws InterruptedException {
        synchronized (lockedKeys) {
            while (!lockedKeys.add(key)) {
                lockedKeys.wait();
            }
        }
    }

    public /*static*/ void unlock(K key) {
        synchronized (lockedKeys) {
            lockedKeys.remove(key);
            lockedKeys.notifyAll();
        }
    }

}

class Test {
    private KeyLock<String> keyLock = new KeyLock<>();

    public void doSynchronouslyOnlyForEqualKeys(String key) throws InterruptedException {
        try {
            keyLock.lock(key);

            //Do what you need with your key.
            //For different keys this part is executed in parallel.
            //For equal keys this part is executed synchronously.

        } finally {
            keyLock.unlock(key);
        }
    }
}