我正在尝试设计一个基于密钥的锁定工具:类似于正常的可重入锁定,而不是lock()和unlock(),你锁定(密钥)和解锁(密钥),合同没有-one如果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();
}
}
答案 0 :(得分:6)
为什么不使用简单的条带锁定,例如:
/**
* 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)];
}
}
答案 1 :(得分:0)
请参考以下示例代码,我为每个线程创建了一个新锁。
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");
}
}
答案 2 :(得分:0)
将其称为DynamicKeyLock. It's a single process lock for any object as key (
等于+
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();
}
}
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();
}
}
简单测试:
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();
}
}
}
答案 3 :(得分:0)
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);
}
}
}