Java 如何用钥匙获得锁
在不锁定整个键值集的情况下,防止同时更新键值集中的一条记录的最佳方法是什么?从语义上讲,我正在寻找某种类型的键锁定(理想情况下是Java实现,但不一定):Java 如何用钥匙获得锁,java,algorithm,synchronization,locking,Java,Algorithm,Synchronization,Locking,在不锁定整个键值集的情况下,防止同时更新键值集中的一条记录的最佳方法是什么?从语义上讲,我正在寻找某种类型的键锁定(理想情况下是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(ssize ssize我编写了一个可以动态锁定任何密钥的类。
它使用一个静态的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);
}
}