Java 如何用钥匙获得锁

Java 如何用钥匙获得锁,java,algorithm,synchronization,locking,Java,Algorithm,Synchronization,Locking,在不锁定整个键值集的情况下,防止同时更新键值集中的一条记录的最佳方法是什么?从语义上讲,我正在寻找某种类型的键锁定(理想情况下是Java实现,但不一定): 此锁用于同步对远程存储的访问,因此某些同步的Java集合不是一个选项。为每个存储桶保留一个互斥锁。这将确保只有冲突等待该互斥对象。如果您提到的“记录”是一个可变对象,“更新”意味着对象的内部状态被修改而不会干扰容器的结构,那么您可以通过锁定记录对象来完成您想要的任务 然而,如果“更新”意味着从容器中删除记录对象并替换它,那么您必须锁定整个容器

在不锁定整个键值集的情况下,防止同时更新键值集中的一条记录的最佳方法是什么?从语义上讲,我正在寻找某种类型的键锁定(理想情况下是Java实现,但不一定):


此锁用于同步对远程存储的访问,因此某些同步的Java集合不是一个选项。

为每个存储桶保留一个互斥锁。这将确保只有冲突等待该互斥对象。

如果您提到的“记录”是一个可变对象,“更新”意味着对象的内部状态被修改而不会干扰容器的结构,那么您可以通过锁定记录对象来完成您想要的任务

然而,如果“更新”意味着从容器中删除记录对象并替换它,那么您必须锁定整个容器,以防止其他线程看到它处于不一致的状态


无论哪种情况,您都应该查看
java.util.concurrent
包中的类。

Guava在13.0中发布了类似的内容;如果你愿意,你可以把它从脑子里弄出来

或多或少地分配特定数量的锁,然后根据其哈希代码将字符串分配给锁。API看起来或多或少像

Striped<Lock> locks = Striped.lock(stripes);
Lock l = locks.get(string);
l.lock();
try {
  // do stuff 
} finally {
  l.unlock();
}
Striped locks=Striped.lock(条纹);
Lock l=locks.get(字符串);
l、 锁();
试一试{
//做事
}最后{
l、 解锁();
}
或多或少,可控的条带数量可以让您在内存使用情况下权衡并发性,因为为每个字符串键分配一个完整的锁可能会很昂贵;本质上,只有在发生哈希冲突时才会发生锁争用,这是(可以预见的)罕见的


(披露:我为番石榴做了贡献。)

这就是为什么;我做到了。是的,我同意如果两个不同的字符串共享相同的哈希代码,那么最终将获得相同的锁

class LockByKey {
    ObjectForString objHolder = new ObjectForString(100);
    public void lockThenWorkForKey (String key) {
        synchronized(objHolder.valueOf(key)){
            //DoSomeWork
        }
    }
}

public final class ObjectForString {

    private final Object[] cache;
    private final int cacheSize;
    final int mask;

    public ObjectForString(int size) {
        // Find power-of-two sizes best matching arguments
        int ssize = 1;
        while (ssize < size) {
            ssize <<= 1;
        }

        mask = ssize - 1;
        cache = new Object[ssize];
        cacheSize = ssize;
        //build the Cache
        for (int i = 0; i < cacheSize; i++) {
            this.cache[i] = new Object();
        }
    }

    public Object valueOf(String key) {
        int index = key.hashCode();
        return cache[index & mask];
    }
}
class LockByKey{
ObjectForString对象文件夹=新ObjectForString(100);
公共无效锁ThenWorkforkey(字符串键){
已同步(objHolder.valueOf(键)){
//DoSomeWork
}
}
}
公共最终类ObjectForString{
私有最终对象[]缓存;
私有最终整数缓存大小;
最终整型掩模;
公共ObjectForString(整型大小){
//找到两个大小的最佳匹配参数的幂
int-ssize=1;
while(ssizessize我编写了一个可以动态锁定任何密钥的类。
它使用一个静态的
CuncurrentHashMap
。但是如果没有使用锁,映射是空的。语法可能会让人混淆,因为它是我们根据密钥创建的新对象。 如果未使用,它会在
unlock
上清除锁。 可以保证,基于两个equal/hascode密钥创建的任何两个
DynamicKeyLock
,它们都将相互锁定

请参阅Java8、Java6的实现和一个小测试

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();
}
@凌驾
公开作废解锁()
{
LockAndCounter LockAndCounter=locksMap.get(钥匙);
lock和counter.lock.unlock();
清洁锁(锁和计数器);
}
@凌驾
public void lockInterruptibly()抛出InterruptedException
{
LockAndCounter LockAndCounter=getLock();
尝试
{
lock和counter.lock.lock();
}
捕捉(中断异常e)
{
清洁锁(锁和计数器);
投掷e;
}
}
@凌驾
公共布尔tryLock()
{
LockAndCounter LockAndCounter=getLock();
boolean=lockAndCounter.lock.tryLock();
如果(!获得)
{
清洁锁(锁和计数器);
}
获得的回报;
}
@凌驾
公共布尔tryLock(longtime,TimeUnit)抛出InterruptedException
{
LockAndCounter LockAndCounter=getLock();
布尔获得;
尝试
{
已获取=lock和counter.lock.tryLock(时间,单位);
}
捕捉(中断异常e)
{
清洁锁(锁和计数器);
投掷e;
}
如果(!获得)
{
清洁锁(锁和计数器);
}
获得的回报;
}
@凌驾
公共条件新条件
{
LockAndCounter LockAndCounter=locksMap.get(钥匙);
return lock和counter.lock.newCondition();
}
}
Java 6:

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();
    }
}
公共类DynamicKeyLock实现锁
{
私有最终静态ConcurrentHashMap locksMap=新ConcurrentHashMap();
私钥;
公共动态锁(T锁钥匙){
this.key=lockKey;
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 DynamicKeyLockTest
{
    @Test
    public void testDifferentKeysDontLock() throws InterruptedException
    {
        DynamicKeyLock<Object> lock = new DynamicKeyLock<>(new Object());
        lock.lock();
        AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
        try
        {
            new Thread(() ->
            {
                DynamicKeyLock<Object> anotherLock = new DynamicKeyLock<>(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();
        DynamicKeyLock<Object> lock = new DynamicKeyLock<>(key);
        lock.lock();
        AtomicBoolean anotherThreadWasExecuted = new AtomicBoolean(false);
        try
        {
            new Thread(() ->
            {
                DynamicKeyLock<Object> anotherLock = new DynamicKeyLock<>(key);
                anotherLock.lock();
                try
                {
                    anotherThreadWasExecuted.set(true);
                }
                finally
                {
                    anotherLock.unlock();
                }
            }).start();
            Thread.sleep(100);
        }
        finally
        {
            Assert.assertFalse(anotherThreadWasExecuted.get());
            lock.unlock();
        }
    }
}
private static final Set<String> lockedKeys = new HashSet<>();

private void lock(String key) throws InterruptedException {
    synchronized (lockedKeys) {
        while (!lockedKeys.add(key)) {
            lockedKeys.wait();
        }
    }
}

private void unlock(String key) {
    synchronized (lockedKeys) {
        lockedKeys.remove(key);
        lockedKeys.notifyAll();
    }
}

public void doSynchronously(String key) throws InterruptedException {
    try {
        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 {
        unlock(key);
    }
}